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 structref readonly structIn a struct declaration to declare a ref struct or a ref readonly struct. 如需詳細資訊,請參閱 ref struct 型別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. 當實值類型由參考傳遞時,沒有 boxing。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 方法。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. 例如,下列陳述式示範了如何定義用於參考值的區域變數值。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 區域變數或 ref readonly 區域變數。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

下面範例會定義具有 TitleAuthor 這兩個 String 欄位的 Book 類別。The following example defines a Book class that has two String fields, Title and Author. 它也會定義 BookCollection 類別,以包含 Book 物件的私用陣列。It also defines a BookCollection class that includes a private array of Book objects. 以傳參考方式傳回個別書籍物件,方法是呼叫其 GetBookByTitle 方法。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 struct 型別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 structYou 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 類型無法實作介面。types cannot implement interfaces.
  • 您不可將 ref struct 宣告為類別或一般結構的成員。You can't declare a ref struct as a member of a class or a normal struct.
  • 您不可在非同步方法中宣告類型為 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 struct 而將它升階成受控堆積。These 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