2014年1月23日 星期四

[Programming] 傳值(pass by value) / 傳參考(pass by reference)

我們在呼叫一些方法(method)的時候,常常會要傳一些參數。然而,傳參數的方法不一樣,對記憶體的操作就不一樣,出來的結果也會有很大的機率是意料之外的不一樣。
.NET所提供傳參數的方式有兩種:

  1. 傳值 (passing by value)
  2. 傳參考 (passing by reference)

 Visual Studio 2010裡,傳值和傳參考的變數型態顏色會不一樣。

傳值:

傳值是很簡單的概念:在進入method之前,程式會在記憶體裡多宣告一塊與參數同樣資料型態的記憶體大小,然後把值複製過去
這塊多出來的記憶體就是這個method專屬使用,method跑完之後就會像垃圾一樣始亂終棄地丟掉了。
不過,並不一定在method跑完之後,這塊記憶體就會馬上被釋放掉。.NET的虛擬機器裡有自己的Garbage Collection機制來處理這些記憶體,理論上而言是不用太擔心這些問題。不過很多系統使用時間一久效能就會由等比級數般的速度變差,不曉得和這部分有沒有關係。


傳參考:

要理解傳參考,得先知道這裡的"參考"是什麼玩意兒。這也是一個我們天天都會用到的東西。
基本上,我們在宣告變數的時候就是在弄一個參考。在沒給值之前這個參考就是指到null,有物件或實值給它的時候它才會把自己到該記憶體的位址上。



知道這個以後,傳參考就變成一件很簡單的概念:它就是把參數的參考複製一份起來給method使用,用完就丟掉。
因為參考實際存的是記憶體位址,所以在method裡操作的參數和呼叫前的物件是同一塊記憶體位址。在Method裡對參數有任何value的變化,都代表著該物件的值是徹底的變了,影響範圍是會擴大到上一層的程式去。



在.NET裡,除了像是int, string, decimal....等諸如此類的最基本的原生資料型態是以傳值方式在做之外,所有物件都是傳參考,包括.NET內建的物件。如果沒掌握好,參數傳一傳很容易出現一些意料之外的bug。
所以如果曾經發生過在傳物件的時候到某某方法去之後值就變了的靈異事件,其實是有科學根據的,沒這麼靈異。


--分隔線--

Q:.NET在傳參考的部分有兩種寫法,如下圖。有什麼差別?


A:
第一種傳參考,編譯器會告訴你在傳參數之前要先經過初始化,不允許傳沒有指到實體物件的"空參考"。
第二種傳參考允許傳空參考進去method,但在離開method之前一定要有給值,不然編譯器會不給過。
除此之外,我不知道有沒有其它實質上的不一樣。



Q:傳參考和C/C++的傳址(Call by Address)有何不同?
A:傳參考是把參考複製一個讓method使用;傳址是更直接地把原本存記憶體位址的那個參考直接拿去給method用傳址是更直接地把原本存記憶體位址直接拿去給method用,而沒有多複製一個新的。

--分隔線--


其實這個觀念我講得有點心虛,當初在學指標的時候也沒有學得很好。如果有觀念上的錯誤請不吝指教。


ref:
[1] 深入淺出C#
[2] C# ref/out 關鍵字與傳遞參考型別參數