ref (C# 參考)

關鍵字 ref 表示變數是參考,或是另一個物件的別名。 它用於五個不同的內容:

  • 在方法簽章和方法呼叫中,以傳址方式將引數傳遞給方法。 如需詳細資訊,請參閱以傳址方式傳遞引數
  • 在方法簽章中,以傳參考方式將值傳回給呼叫者。 如需詳細資訊,請參閱參考傳回值
  • 在成員主體中,指出參考傳回值儲存在本機作為呼叫者想要修改的參考。 或者,表示區域變數會依參考存取另一個值。 如需詳細資訊,請參閱 ref 區域變數
  • struct 宣告中,宣告 ref structreadonly ref struct 。 如需詳細資訊,請參閱 ref struct 文章。
  • ref struct在宣告中,宣告欄位為參考。 ref 請參閱欄位文章。

以傳址方式傳遞引數

用於方法的參數清單時,ref 關鍵字指出以傳參考方式傳遞引數,而不是以傳值方式。 ref 關鍵字會使形式參數成為引數的別名,其必須為變數。 換句話說,參數上的任何作業都會在引數上進行。

例如,假設呼叫端傳遞區域變數運算式或陣列元素存取運算式。 接著,呼叫的方法可以取代 ref 參數所參考的物件。 在此情況下,呼叫端的區域變數或陣列專案會在方法傳回時參考新的 物件。

注意

請勿混淆以傳址方式傳遞的概念與參考型別的概念。 兩個概念並不相同。 方法參數可以由 ref 修改,而不論其是否為實值類型或參考類型。 當實值類型由參考傳遞時,沒有 boxing。

若要使用 ref 參數,方法定義和呼叫方法都必須明確使用 ref 關鍵字,如下列範例所示。 (除了在進行 COM call.) 時,呼叫方法可以省略 ref

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

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

傳遞至 refin 參數的引數必須在傳遞之前初始化。 這項需求不同于 out 參數,其引數不需要在傳遞之前明確初始化。

類別的成員的簽章,不能只有在 refinout 部分不同。 如果類型的兩個成員之間,唯一的區別在於其中一個有 ref 參數,而另一個有 outin 參數,則會發生編譯器錯誤。 例如,下列程式碼不會進行編譯。

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 參數,而另一個方法具有以值傳遞的參數時,可以多載方法,如下列範例所示。

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

其他需要簽章比對的情況 (像是隱藏或覆寫),inrefout 是簽章的一部分,但彼此不相符。

屬性不是變數。 它們是方法,無法傳遞至 ref 參數。

您不可為下列幾種方法使用 refinout 關鍵字:

  • 使用 async 修飾詞定義的 async 方法。
  • 迭代器方法,其包括 yield returnyield break 陳述式。

擴充方法 也有使用這些關鍵字的限制:

  • out關鍵字不能用於擴充方法的第一個引數。
  • ref當引數不是結構,或泛型型別不受限制為結構時,無法在擴充方法的第一個引數上使用 關鍵字。
  • in除非第一個引數是結構,否則無法使用 關鍵字。 in即使限制為 結構,關鍵字也無法用於任何泛型型別。

以傳址方式傳遞引數:範例

先前的範例會以傳參考方式傳遞實值型別。 您也可以使用 ref 關鍵字,以傳參考方式傳遞參考型別。 以傳址方式傳遞參考型別,可讓已呼叫方法取代參考參數在呼叫者中所參考的物件。 物件的儲存位置會以參考參數值的方式,傳遞至方法。 如果您變更參數儲存位置中的值 (指向新的物件),則也會變更呼叫端所參考的儲存位置。 下列範例會以 ref 參數,傳遞參考類型的執行個體。

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

如需如何依傳值和依傳址方式來傳遞參考類型的詳細資訊,請參閱傳遞參考類型參數

參考傳回值

參考傳回值 (或 ref 傳回值) 是方法以傳參考方式傳回給呼叫者的值。 也就是說,呼叫端可以修改方法所傳回的值,而該變更會反映在所呼叫方法中物件的狀態。

參考傳回值是使用 ref 關鍵字所定義:

  • 在方法簽章中。 例如,下列方法簽章指出 GetCurrentPrice 方法以傳參考方式傳回 Decimal 值。
public ref decimal GetCurrentPrice()
  • return 權杖與方法之 return 陳述式中所傳回變數之間。 例如:
return ref DecimalArray[0];

為了讓呼叫者修改物件的狀態,參考傳回值必須儲存至明確定義為 ref 區域變數的變數。

以下是更完整的 ref 傳回範例,其中顯示方法簽章和方法主體。

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 ,以傳回傳址方式傳回值,並強制呼叫程式碼無法修改傳回的值。 呼叫方法可以避免複製傳回的值,方法是將值儲存在 本機 ref readonly 變數中。

如需範例,請參閱 ref 傳回值和 ref 區域變數範例

ref 區域變數

ref 區域變數用來參考使用 return ref 所傳回的值。 ref 區域變數無法初始化為非 ref 傳回值。 換句話說,初始化的右側必須是參考。 任何對 ref 區域變數值進行的修改,都會反映在其方法以傳址方式傳回值之物件的狀態。

您可以在兩個地方使用 ref 關鍵字來定義 ref local:

  • 變數宣告之前。
  • 緊接在呼叫以傳址方式傳回值的方法之前。

例如,下列陳述式定義名為 GetEstimatedValue 之方法所傳回的 ref 區域變數值:

ref decimal estValue = ref Building.GetEstimatedValue();

您可透過相同方式以參考存取值。 在某些情況下,以參考存取值會避免潛在過度浪費資源的複製作業,進而增加效能。 例如,下列語句示範如何定義用來參考值的 ref 區域變數。

ref VeryLargeStruct reflocal = ref veryLargeStruct;

在這兩個範例中, ref 關鍵字都必須用於這兩個位置,或者編譯器會產生錯誤 CS8172:「無法以值初始化參考變數」。

語句的 foreach 反覆運算變數可以是 ref 區域變數或 ref 唯讀區域變數。 如需詳細資訊,請參閱 foreach 陳述式一文。 您可以使用 ref 指派運算子重新指派 ref 區域變數或 ref 唯讀區域變數。

ref readonly 區域變數

ref readonly local 是用來參考方法或屬性 ref readonly 在其簽章中傳回的值,並使用 return ref 。 變數 ref readonly 結合了區域變數的屬性 refreadonly 變數:它是指派給其儲存體的別名,而且無法修改。

ref 傳回值和 ref 區域變數範例

下面範例會定義具有 TitleAuthor 這兩個 String 欄位的 Book 類別。 它也會定義 BookCollection 類別,以包含 Book 物件的私用陣列。 以傳址方式傳回個別書籍物件,方法是呼叫其 GetBookByTitle 方法。


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 物件中,如下列範例所示。

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 ,您可以宣告屬於欄位的 ref 欄位。 ref 欄位只有在類型中 ref struct 才有效,以確保參考不會逾時所參考的物件。 這項功能會啟用類型,例如 System.Span<T>

public readonly ref struct Span<T>
{
    internal readonly ref T _reference;
    private readonly int _length;

    // Omitted for brevity...
}

Span<T> 別會儲存其存取連續元素的參考。 參考可讓 Span<T> 物件避免複製它所參考的儲存體。

C# 語言規格

如需詳細資訊,請參閱 C# 語言規格。 語言規格是 C# 語法及用法的限定來源。

另請參閱