ref (C# 參考)

ref關鍵字表示值是以傳址方式傳遞。 它用於三個不同的內容:

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

以傳址方式傳遞引數

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

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

注意

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

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

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) { }
}

不過,當一個方法具有、或參數,而另一個方法有以傳值方式傳遞的參數時,可以多載方法 ref in out ,如下列範例所示。

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 區域變數:

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

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

ref decimal estValue = ref Building.GetEstimatedValue();

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

ref VeryLargeStruct reflocal = ref veryLargeStruct;

在這兩個範例中 ref ,關鍵字都必須用於這兩個位置,否則編譯器會產生錯誤 CS8172 「無法使用值將傳址變數初始化」。

從 c # 7.3 開始,語句的反覆運算變數 foreach 可以是 ref 區域變數或 ref readonly 本機變數。 如需詳細資訊,請參閱 foreach 陳述式一文。

此外,從 c # 7.3 開始,您可以使用 ref 指派運算子重新指派 ref 區域變數或 ref readonly 區域變數。

ref readonly 區域變數

Ref readonly 區域變數是用來參考其簽章和使用的方法或屬性所傳回的值 ref readonly return refref 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

C# 語言規格

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

另請參閱