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 the ref struct section of the Structure types article.

以傳址方式傳遞引數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.

此外, 擴充方法 具有下列限制:In addition, extension methods have the following restrictions:

  • out關鍵字不能用在擴充方法的第一個引數上。The out keyword cannot be used on the first argument of an extension method.
  • ref當引數不是結構,或是不受限於不是結構的泛型型別時,無法在擴充方法的第一個引數上使用關鍵字。The ref keyword cannot be used on the first argument of an extension method when the argument is not a struct, or a generic type not constrained to be a struct.
  • in除非第一個引數是結構,否則無法使用關鍵字。The in keyword cannot be used unless the first argument is a struct. in關鍵字不能用於任何泛型型別,即使當條件約束為結構時也一樣。The in keyword cannot be used on any generic type, even when constrained to be a struct.

以傳址方式傳遞引數:範例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 in the calling 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 傳回範例,同時顯示方法簽章和方法主體。Here is a more complete ref return example, showing both the method signature and method body.

public static ref int Find(int[,] matrix, Func<int, bool> predicate)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");
}

所呼叫的方法也可能將傳回值宣告為 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 value 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 「無法使用值將傳址變數初始化」。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.

此外,從 c # 7.3 開始,您可以使用 ref 指派運算子重新指派 ref 區域變數或 ref readonly 區域變數。Also beginning with C# 7.3, you can reassign a ref local or ref readonly local variable with the ref assignment operator.

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

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