C#中有關於方法中的參數物件傳遞之觀念釐清

我們都知道C#中有兩大型別,一種是實質型別(ValueType),例如:int、char、enum、struct...等;另外一種是參考型別(ReferenceType),諸如你看到所有的class、string、delegate都算是參考型別。

今天有一位朋友遇到方法的參數傳遞方面的觀念問題,因此特地寫了這篇文章來記錄一下。

首要心法

只要你確定程序內調用某方法之傳過去的引數(Argument)是屬於哪一種型別,那麼方法裡面的參數(Parameter)就會以那個型別的方式接收。例如,如果引數是int,那麼方法內接收的參數,就會被複製一份實質過去。反之,如果是參考型別,則會把這個參考的物件位址帶過去方法內的接收參數。

有了這個基本心法後,再來看一下下面這張程式碼圖片。

我這邊就順著程式碼,口語的方式把這個觀念順一次。

那麼,如何在方法之間,讓傳遞的參數永遠指向同一個記憶體位址?

這問題可以改變成另外一種說法,就是我如何把方法中拿到的參數,永遠不要變更參考的記憶體位址,而是視為可以直接操作不斷開關聯的一個參考變數呢?

答案很簡單,在引數與參數的開頭,都加上「ref」關鍵字即可。例如:

private void Method1()
{
  System.Collections.Hashtable oHT = new System.Collections.Hashtable();
  oHT.Add("A", 1);
  Method2(ref oHT);
  foreach (var oItem in oHT.Keys)  { Console.WriteLine(oHT[oItem]); }
}
private void Method2(ref System.Collections.Hashtable oTemp)
{
  oTemp.Add("B", 2);
  oTemp = null;
}

上面的程式碼中,引數與參數的開頭都被加入了ref關鍵字,代表在跟編譯器講說,以傳址的方式幫我傳遞參數,那麼,所有的參數都會被鎖住不能夠隨意的改參考到別的記憶體空間,所以當oTemp = null這一行出現的時候,也就代表了oHT的死亡。因此,在最後foreach oHT這行程式,你肯定會得到一個System.NullReferenceException。

如果你今天是在操作實質型別的參數傳遞,依然適用這個觀念喔!(不過,上面講的這些觀念,用在實質型別身上本來就天經地義了,所以好像也不用特別去在意就是了。)

跨類別共同參考某個物件,依然適用此思維

請參考下列程式碼,由TestDT類別產生並拋出HashTable,然後TestDT被using自我Dispose,結果最終參考到的HashTable依然可以被列印出內容(但是TestDT類別已經自爆了)。

class Program
{
  public static void Main()
  {
    Hashtable oHT = null;

    using (TestDT oTemp = new TestDT())
    { oHT = oTemp.GetHT(); }

    foreach (var item in oHT.Keys)
    { Console.WriteLine(oHT[item]); }

    Console.Read();
  }
}

class TestDT : IDisposable
{
  private Hashtable _oHT = new Hashtable();

  public TestDT()
  {
    for (int i = 0; i < 10; i++)
    {
      _oHT.Add(System.Guid.NewGuid().ToString(), System.Guid.NewGuid().ToString());
    }
  }
  
  public Hashtable GetHT()
  { return _oHT; }

  public void Dispose()
  {
    _oHT = null;
    GC.SuppressFinalize(this);
  }
}

以上為C#之參數傳遞觀念小複習。

C# Parameter Argument PassingValueTypesByValue PassingValueTypesByReference PassingReferenceTypesByValue PassingReferenceTypesByReference