首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

深入理解 C# ref 參數與參考型別的行為差異

     編輯:關於C語言

前言

在學習 C# 參數傳遞時,書籍上關於 ref 關鍵字的說明通常很直觀:加上 ref 後,方法內部對參數的修改會影響外部變數的值。然而,實際開發中經常會遇到一個令人困惑的現象——陣列(以及其他參考型別)在不加 ref 的情況下,其元素值也能被修改。本文將深入剖析這背後的機制,釐清「傳值」與「傳引用」的真正含義。

教科書式的 ref 範例

先回顧經典範例,說明 ref 對實值型別(如 string、int)的影響:

public static void ValueParam(string str)
{
    str = "251"; // 嘗試修改參數指向
}

public static void RefParam(ref string str)
{
    str = "250"; // 修改參數指向,會影響外部變數
}

public static void Main()
{
    string str = "249";
    ValueParam(str);
    Console.WriteLine("Value Param: " + str); // 輸出:249

    RefParam(ref str);
    Console.WriteLine("Ref Param: " + str);   // 輸出:250
}

結果解析:string 是不可變的參考型別,但預設以「傳值」方式傳遞(傳遞的是參考的副本)。在 ValueParam 中重新賦值只是改變了區域變數指向的物件,不影響外部;而 RefParam 加上 ref 後,直接操作外部變數的儲存位置,因此能修改其指向。

陣列參數的「反直覺」行為

現在看看陣列參數:

public void ChangeValue(int[] arr)
{
    for (int i = 0; i < arr.Length; i++)
        arr[i] = 0; // 修改陣列「元素」
}

public static void Main()
{
    int[] arr = { 3, 4, 5, 7, 2, 6, 1 };
    // 輸出原始值...
    ChangeValue(arr); // 未使用 ref
    // 輸出修改後的值:全變成 0
}

現象:陣列元素被歸零,雖然沒加 ref。

核心原理:參考型別的「傳值」

這並非例外,而是 C# 參數傳遞機制的本質體現:

  • 預設行為(By Value):方法接收的是「參考的副本」。副本與原變數指向同一個堆上的物件。
  • 修改物件內部狀態(如 arr[i] = 0、list.Add()、obj.Property = value):透過副本存取同一物件,**會影響外部可見的物件內容**。
  • 修改參數指向(如 arr = new int[5]、str = "new"):只是讓區域參數指向新物件,**不影響外部變數**。

ref 的作用是:**讓參數成為外部變數的別名**,任何對參數指向的修改(重新賦值)都會直接反映到外部變數。

對比總結表

操作類型實值型別 (int, struct)參考型別 (class, array, string)加上 ref 後
修改內部資料/欄位/元素不影響外部影響外部影響外部
重新賦值 (指向新物件/新值)不影響外部不影響外部影響外部

實務建議

  1. 不需要修改參數指向時,勿濫用 ref。參考型別傳遞參考副本已足夠高效且語意清晰。
  2. 需讓方法「換掉」外部變數指向的物件(如工廠方法回傳新實例、TryParse 模式)時,才使用 ref 或 out。
  3. 文件化意圖:若方法會大幅修改傳入物件的狀態,在 XML 註解說明,避免呼叫端誤以為物件不會被改動。

結語

理解「傳遞參考的副本」與「傳遞變數的別名」的區別,是掌握 C# 參數傳遞的關鍵。陣列元素可被修改並非特例,而是參考型別語意的自然結果。下次看到「沒加 ref 卻改了值」,請記住:你改的是「物件內容」,不是「變數指向」。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved