ref(C# 参考)ref (C# Reference)

ref 关键字指示按引用传递的值。The ref keyword indicates a value that is passed by reference. 它用在四种不同的上下文中:It is used in four different contexts:

  • 在方法签名和方法调用中,按引用将参数传递给方法。In a method signature and in a method call, to pass an argument to a method by reference. 有关详细信息,请参阅按引用传递参数For more information, see Passing an argument by reference.
  • 在方法签名中,按引用将值返回给调用方。In a method signature, to return a value to the caller by reference. 有关详细信息,请参阅引用返回值For more information, see Reference return values.
  • 在成员正文中,指示引用返回值是否作为调用方欲修改的引用被存储在本地,或在一般情况下,局部变量按引用访问另一个值。In a member body, to indicate that a reference return value is stored locally as a reference that the caller intends to modify or, in general, a local variable accesses another value by reference. 有关详细信息,请参阅 Ref 局部变量For more information, see Ref locals.
  • struct 声明中声明 ref structreadonly ref structIn a struct declaration to declare a ref struct or a readonly ref struct. 有关详细信息,请参阅 ref 结构类型For more information, see ref struct types.

按引用传递参数Passing an argument by reference

在方法的参数列表中使用 ref 关键字时,它指示参数按引用传递,而非按值传递。When used in a method's parameter list, the ref keyword indicates that an argument is passed by reference, not by value. ref 关键字让形参成为实参的别名,这必须是变量。The ref keyword makes the formal parameter an alias for the argument, which must be a variable. 换而言之,对形参执行的任何操作都是对实参执行的。In other words, any operation on the parameter is made on the argument. 例如,如果调用方传递本地变量表达式或数组元素访问表达式,所调用方法会替换 ref 参数引用的对象,然后,当该方法返回时,调用方的本地变量或数组元素将开始引用新对象。For example, if the caller passes a local variable expression or an array element access expression, and the called method replaces the object to which the ref parameter refers, then the caller’s local variable or the array element now refers to the new object when the method returns.

备注

不要混淆通过引用传递的概念与引用类型的概念。Do not confuse the concept of passing by reference with the concept of reference types. 这两种概念是不同的。The two concepts are not the same. 无论方法参数是值类型还是引用类型,均可由 ref 修改。A method parameter can be modified by ref regardless of whether it is a value type or a reference type. 当通过引用传递时,不会对值类型装箱。There is no boxing of a value type when it is passed by reference.

若要使用 ref 参数,方法定义和调用方法均必须显式使用 ref 关键字,如下面的示例所示。To use a ref parameter, both the method definition and the calling method must explicitly use the ref keyword, as shown in the following example.

void Method(ref int refArgument)
{
    refArgument = refArgument + 44;
}

int number = 1;
Method(ref number);
Console.WriteLine(number);
// Output: 45

传递到 refin 形参的实参必须先经过初始化,然后才能传递。An argument that is passed to a ref or in parameter must be initialized before it is passed. 这与 out 形参不同,在传递之前,不需要显式初始化该形参的实参。This differs from out parameters, whose arguments do not have to be explicitly initialized before they are passed.

类的成员不能具有仅在 refinout 方面不同的签名。Members of a class can't have signatures that differ only by ref, in, or out. 如果类型的两个成员之间的唯一区别在于其中一个具有 ref 参数,而另一个具有 outin 参数,则会发生编译器错误。A compiler error occurs if the only difference between two members of a type is that one of them has a ref parameter and the other has an out, or in parameter. 例如,以下代码将不会编译。The following code, for example, doesn't compile.

class CS0663_Example
{
    // Compiler error CS0663: "Cannot define overloaded 
    // methods that differ only on ref and out".
    public void SampleMethod(out int i) { }
    public void SampleMethod(ref int i) { }
}

但是,当一个方法具有 refinout 参数,另一个方法具有值参数时,则可以重载方法,如下面的示例所示。However, methods can be overloaded when one method has a ref, in, or out parameter and the other has a value parameter, as shown in the following example.

class RefOverloadExample
{
    public void SampleMethod(int i) { }
    public void SampleMethod(ref int i) { }
}

在其他要求签名匹配的情况下(如隐藏或重写),inrefout 是签名的一部分,相互之间不匹配。In other situations that require signature matching, such as hiding or overriding, in, ref, and out are part of the signature and don't match each other.

属性不是变量。Properties are not variables. 它们是方法,不能传递到 ref 参数。They are methods, and cannot be passed to ref parameters.

不能将 refinout 关键字用于以下几种方法:You can't use the ref, in, and out keywords for the following kinds of methods:

  • 异步方法,通过使用 async 修饰符定义。Async methods, which you define by using the async modifier.
  • 迭代器方法,包括 yield returnyield break 语句。Iterator methods, which include a yield return or yield break statement.

按引用传递参数:示例Passing an argument by reference: An example

前面的示例按引用传递值类型。The previous examples pass value types by reference. 还可使用 ref 关键字按引用传递引用类型。You can also use the ref keyword to pass reference types by reference. 按引用传递引用类型使所调用方能够替换调用方中引用参数引用的对象。Passing a reference type by reference enables the called method to replace the object to which the reference parameter refers in the caller. 对象的存储位置按引用参数的值传递到方法。The storage location of the object is passed to the method as the value of the reference parameter. 如果更改参数存储位置中的值(以指向新对象),你还可以将存储位置更改为调用方所引用的位置。If you change the value in the storage location of the parameter (to point to a new object), you also change the storage location to which the caller refers. 下面的示例将引用类型的实例作为 ref 参数传递。The following example passes an instance of a reference type as a ref parameter.

class Product
{
    public Product(string name, int newID)
    {
        ItemName = name;
        ItemID = newID;
    }

    public string ItemName { get; set; }
    public int ItemID { get; set; }
}

private static void ChangeByReference(ref Product itemRef)
{
    // Change the address that is stored in the itemRef parameter.   
    itemRef = new Product("Stapler", 99999);

    // You can change the value of one of the properties of
    // itemRef. The change happens to item in Main as well.
    itemRef.ItemID = 12345;
}

private static void ModifyProductsByReference()
{
    // Declare an instance of Product and display its initial values.
    Product item = new Product("Fasteners", 54321);
    System.Console.WriteLine("Original values in Main.  Name: {0}, ID: {1}\n",
        item.ItemName, item.ItemID);

    // Pass the product instance to ChangeByReference.
    ChangeByReference(ref item);
    System.Console.WriteLine("Back in Main.  Name: {0}, ID: {1}\n",
        item.ItemName, item.ItemID);
}

// This method displays the following output:
// Original values in Main.  Name: Fasteners, ID: 54321
// Back in Main.  Name: Stapler, ID: 12345        

有关如何通过值和引用传递引用类型的详细信息,请参阅传递引用类型参数For more information about how to pass reference types by value and by reference, see Passing Reference-Type Parameters.

引用返回值Reference return values

引用返回值(或 ref 返回值)是由方法按引用向调用方返回的值。Reference return values (or ref returns) are values that a method returns by reference to the caller. 即是说,调用方可以修改方法所返回的值,此更改反映在包含方法的对象的状态中。That is, the caller can modify the value returned by a method, and that change is reflected in the state of the object that contains the method.

使用 ref 关键字来定义引用返回值:A reference return value is defined by using the ref keyword:

  • 在方法签名中。In the method signature. 例如,下列方法签名指示 GetCurrentPrice 方法按引用返回了 Decimal 值。For example, the following method signature indicates that the GetCurrentPrice method returns a Decimal value by reference.
public ref decimal GetCurrentPrice()
  • return 标记和方法的 return 语句中返回的变量之间。Between the return token and the variable returned in a return statement in the method. 例如:For example:
return ref DecimalArray[0];

为方便调用方修改对象的状态,引用返回值必须存储在被显式定义为 ref 局部变量的变量中。In order for the caller to modify the object's state, the reference return value must be stored to a variable that is explicitly defined as a ref local.

所调用方法还可能会将返回值声明为 ref readonly 以按引用返回值,并坚持调用代码无法修改返回的值。The called method may also declare the return value as ref readonly to return the value by reference, and enforce that the calling code cannot modify the returned value. 调用方法可以通过将返回的值存储在本地 ref readonly 变量中来避免复制该值。The calling method can avoid copying the returned valued by storing the value in a local ref readonly variable.

有关示例,请参阅 ref 返回值和 ref 局部变量示例For an example, see A ref returns and ref locals example.

ref 局部变量Ref locals

ref 局部变量用于指代使用 return ref 返回的值。A ref local variable is used to refer to values returned using return ref. 无法将 ref 局部变量初始化为非 ref 返回值。A ref local variable cannot be initialized to a non-ref return value. 也就是说,初始化的右侧必须为引用。In other words, the right hand side of the initialization must be a reference. 任何对 ref 本地变量值的修改都将反映在对象的状态中,该对象的方法按引用返回值。Any modifications to the value of the ref local are reflected in the state of the object whose method returned the value by reference.

在变量声明前或在方法(该方法将按引用返回值)调用前使用 ref 关键字定义 ref 局部变量。You define a ref local by using the ref keyword before the variable declaration, as well as immediately before the call to the method that returns the value by reference.

例如,下列语句定义名为 GetEstimatedValue 的方法返回的 ref 局部变量值:For example, the following statement defines a ref local value that is returned by a method named GetEstimatedValue:

ref decimal estValue = ref Building.GetEstimatedValue();

可通过相同方式按引用访问值。You can access a value by reference in the same way. 在某些情况下,按引用访问值可避免潜在的高开销复制操作,从而提高性能。In some cases, accessing a value by reference increases performance by avoiding a potentially expensive copy operation. 例如,以下语句显示用户可如何定义一个用于引用值的 ref 局部变量值。For example, the following statement shows how one can define a ref local value that is used to reference a value.

ref VeryLargeStruct reflocal = ref veryLargeStruct;

请注意,在这两个示例中,必须在两个位置同时使用 ref 关键字,否则编译器将生成错误 CS8172:“无法使用值对按引用变量进行初始化”。Note that in both examples the ref keyword must be used in both places, or the compiler generates error CS8172, "Cannot initialize a by-reference variable with a value."

从 C# 7.3 开始,foreach 语句的迭代变量可以是 ref local 变量,也可以是 ref readonly local 变量。Beginning with C# 7.3, the iteration variable of the foreach statement can be ref local or ref readonly local variable. 有关详细信息,请参阅 foreach 语句一文。For more information, see the foreach statement article.

Ref readonly 局部变量Ref readonly locals

Ref readonly 局部变量用于指代在其签名中具有 ref readonly 并使用 return ref 的方法或属性返回的值。A ref readonly local is used to refer to values returned by the method or property that has ref readonly in its signature and uses return ref. ref readonly 变量将 ref 本地变量的属性与 readonly 变量结合使用:它是所分配到的存储的别名,且无法修改。A ref readonly variable combines the properties of a ref local variable with a readonly variable: it is an alias to the storage it's assigned to, and it cannot be modified.

ref 返回值和 ref 局部变量示例A ref returns and ref locals example

下列示例定义一个具有两个 String 字段(TitleAuthor)的 Book 类。The following example defines a Book class that has two String fields, Title and Author. 还定义包含 Book 对象的专用数组的 BookCollection 类。It also defines a BookCollection class that includes a private array of Book objects. 通过调用 GetBookByTitle 方法,可按引用返回个别 book 对象。Individual book objects are returned by reference by calling its GetBookByTitle method.


public class Book
{
    public string Author;
    public string Title;
}

public class BookCollection
{
    private Book[] books = { new Book { Title = "Call of the Wild, The", Author = "Jack London" },
                        new Book { Title = "Tale of Two Cities, A", Author = "Charles Dickens" }
                       };
    private Book nobook = null;

    public ref Book GetBookByTitle(string title)
    {
        for (int ctr = 0; ctr < books.Length; ctr++)
        {
            if (title == books[ctr].Title)
                return ref books[ctr];
        }
        return ref nobook;
    }

    public void ListBooks()
    {
        foreach (var book in books)
        {
            Console.WriteLine($"{book.Title}, by {book.Author}");
        }
        Console.WriteLine();
    }
}

调用方将 GetBookByTitle 方法所返回的值存储为 ref 局部变量时,调用方对返回值所做的更改将反映在 BookCollection 对象中,如下例所示。When the caller stores the value returned by the GetBookByTitle method as a ref local, changes that the caller makes to the return value are reflected in the BookCollection object, as the following example shows.

var bc = new BookCollection();
bc.ListBooks();

ref var book = ref bc.GetBookByTitle("Call of the Wild, The");
if (book != null)
    book = new Book { Title = "Republic, The", Author = "Plato" };
bc.ListBooks();
// The example displays the following output:
//       Call of the Wild, The, by Jack London
//       Tale of Two Cities, A, by Charles Dickens
//       
//       Republic, The, by Plato
//       Tale of Two Cities, A, by Charles Dickens

Ref 结构类型Ref struct types

ref 修饰符添加到 struct 声明定义了该类型的实例必须为堆栈分配。Adding the ref modifier to a struct declaration defines that instances of that type must be stack allocated. 换言之,永远不能在作为另一类的成员的堆上创建这些类型的实例。In other words, instances of these types can never be created on the heap as a member of another class. 此功能的主要动机是 Span<T> 和相关结构。The primary motivation for this feature was Span<T> and related structures.

保持 ref struct 类型作为堆栈分配的变量的目标引入了几条编译器针对所有 ref struct 类型强制执行的规则。The goal of keeping a ref struct type as a stack-allocated variable introduces several rules that the compiler enforces for all ref struct types.

  • 不能对 ref struct 装箱。You can't box a ref struct. 无法向属于 objectdynamic 或任何接口类型的变量分配 ref struct 类型。You cannot assign a ref struct type to a variable of type object, dynamic, or any interface type.
  • ref struct 类型不能实现接口。ref struct types cannot implement interfaces.
  • 不能将 ref struct 声明为类或常规结构的字段成员。You can't declare a ref struct as a field member of a class or a normal struct. 这包括声明自动实现的属性,后者会创建一个由编译器生成的支持字段。This includes declaring an auto-implemented property, which creates a compiler generated backing field.
  • 不能声明异步方法中属于 ref struct 类型的本地变量。You cannot declare local variables that are ref struct types in async methods. 不能在返回类似 TaskTask<TResult>Task 类型的同步方法中声明它们。You can declare them in synchronous methods that return Task, Task<TResult> or Task-like types.
  • 无法在迭代器中声明 ref struct 本地变量。You cannot declare ref struct local variables in iterators.
  • 无法捕获 Lambda 表达式或本地函数中的 ref struct 变量。You cannot capture ref struct variables in lambda expressions or local functions.

这些限制可确保不会以可提升至托管堆的方式意外地使用 ref structThese restrictions ensure you don't accidentally use a ref struct in a manner that could promote it to the managed heap.

可以组合修饰符以将结构声明为 readonly refYou can combine modifiers to declare a struct as readonly ref. readonly ref struct 兼具 ref structreadonly struct 声明的优点和限制。A readonly ref struct combines the benefits and restrictions of ref struct and readonly struct declarations.

C# 语言规范C# language specification

有关详细信息,请参阅 C# 语言规范For more information, see the C# Language Specification. 该语言规范是 C# 语法和用法的权威资料。The language specification is the definitive source for C# syntax and usage.

请参阅See also