Call By Value 與 Call By Reference

  • 4412
  • 0

C# 定義數值 Call By Value,物件 Call By Reference,在使用 Lambda Expression 傳參數時,變數是 "參考指標",可能會與預期不同

學 C# 的第一天起,就深植了這個印像:參數若是數值是 Call By Value,
若是物件則是 Call By Reference。

 

較精確的說法:


C# 的參數有數種傳遞方式,包含傳值參數 (call by value),傳址參數 (call by reference) 等,基本型態的參數,像是 int, double, char, … 等,預設都使用傳值的方式,而物件形態的參數,像是 StringBuilder,陣列等,預設則是使用傳址的方式。


以上摘錄自: http://cs0.wikidot.com/function

 

筆者遇到一個看似推翻上述的案例,使用 Lambda Expressions 傳入數值,竟然是以 Call By Reference 方式,發生不如預期的程式碼如下


public void Test(int numSites)
{
    System.Timers.Timer[] scanLiveTimer = new System.Timers.Timer[numSites];

    for (int si = 0; si < numSites; si++)
    {
        scanLiveTimer[si] = new System.Timers.Timer();

        scanLiveTimer[si].Elapsed +=
            new System.Timers.ElapsedEventHandler((sender, e) => ScanMarketEvent(si));

        scanLiveTimer[si].Interval = 500;
        scanLiveTimer[si].Start();
        Console.WriteLine(string.Format("Trigger: {0}", si));
    }
}

void ScanMarketEvent(int si)
{
    // 有誤,讀到的值都一樣
    Console.Write(string.Format("{0} ", si));
}

在 第 10 行明確傳入 int 數值,但執行結果是等於 si 在迴圈後的值

---> 這看起是 Call By Reference 的結果

 

經過小調整後,在使用 Lambda Expressions 先 複製 成另一個數值再傳入,執行可以得到預期結果


public void Test(int numSites)
{
    System.Timers.Timer[] scanLiveTimer = new System.Timers.Timer[numSites];

    for (int si = 0; si < numSites; si++)
    {
        scanLiveTimer[si] = new System.Timers.Timer();
        // 複製變數,再傳入 lambda expression
        int x = si;

        scanLiveTimer[si].Elapsed +=
            new System.Timers.ElapsedEventHandler((sender, e) => ScanMarketEvent(x));

        scanLiveTimer[si].Interval = 500;
        scanLiveTimer[si].Start();
        Console.WriteLine(string.Format("Trigger: {0}", si));
    }
}

void ScanMarketEvent(int si)
{
    // 可以讀到正確的值
    Console.Write(string.Format("{0} ", si));
}

 

使用反組譯工具發現,使用 Lambda Expressions 額外多了一個類別 c__DisplayClass2

image

 

筆者猜測在執行時期,有可能是透過此類別做為傳遞,

因此仍不違反 "數值是 Call By Value,若是物件則是 Call By Reference" 的說法。

 

測試案例:下載原始檔  or 直接瀏覽Gist