[你必須知道的.NET] 第十二回:參數之惑---傳遞的藝術(下)
[2] [你必須知道的.NET] 第十二回:參數之惑---傳遞的藝術(下)
系列文章導航:
[你必須知道的.NET] 第四回:后來居上:class和struct
[你必須知道的.NET] 第五回:深入淺出關鍵字---把new說透
[你必須知道的.NET] 第六回:深入淺出關鍵字---base和this
[你必須知道的.NET] 第七回:品味類型---從通用類型系統開始
[你必須知道的.NET] 第八回:品味類型---值類型與引用類型(上)-內存有理
[你必須知道的.NET] 第九回:品味類型---值類型與引用類型(中)-規則無邊
[你必須知道的.NET] 第十回:品味類型---值類型與引用類型(下)-應用征途
[你必須知道的.NET] 第十一回:參數之惑---傳遞的藝術(上)
[你必須知道的.NET] 第十二回:參數之惑---傳遞的藝術(下)
[你必須知道的.NET] 第十三回:從Hello, world開始認識IL
[你必須知道的.NET] 第十四回:認識IL代碼---從開始到現在
[你必須知道的.NET] 第十六回:深入淺出關鍵字---using全接觸
[你必須知道的.NET]第二十二回:字符串駐留(上)---帶著問題思考
[你必須知道的.NET]第三十二回,深入.NET 4.0之,Tuple一二
本文將介紹以下內容:
• 按值傳遞與按引用傳遞深論
• ref和out比較
• 參數應用淺析
接上篇繼續,『第十一回:參數之惑---傳遞的藝術(上)』
4.2 引用類型參數的按值傳遞
當傳遞的參數為引用類型時,傳遞和操作的是指向對象的引用,這意味著方法操作可以改變原來的對象,但是值得思考的是該引用或者說指針本身還是按值傳遞的。因此,我們在此必須清楚的了解以下兩個最根本的問題:
• 引用類型參數的按值傳遞和按引用傳遞的區別?
• string類型作為特殊的引用類型,在按值傳遞時表現的特殊性又如何解釋?
首先,我們從基本的理解入手來了解引用類型參數按值傳遞的本質所在,簡單的說對象作為參數傳遞時,執行的是對對象地址的拷貝,操作的是該拷貝地址。這在本質上和值類型參數按值傳遞是相同的,都是按值傳遞。不同的是值類型的“值”為類型實例,而引用類型的“值”為引用地址。因此,如果參數為引用類型時,在調用方代碼中,可以改變引用的指向, 從而使得原對象的指向發生改變,如例所示:
引用類型參數的按值傳遞
因此,我們進一步可以總結為:按值傳遞的實質的是傳遞值,不同的是這個值在值類型和引用類型的表現是不同的:參數為值類型時,“值”為實例本身,因此傳遞的是實例拷貝,不會對原來的實例產生影響;參數為引用類型時,“值”為對象引用,因此傳遞的是引用地址拷貝,會改變原來對象的引用指向,這是二者在統一概念上的表現區別,理解了本質也就抓住了根源。關于值類型和引用類型的概念可以參考《第八回:品味類型---值類型與引用類型(上)-內存有理》《第九回:品味類型---值類型與引用類型(中)-規則無邊》《第十回:品味類型---值類型與引用類型(下)-應用征途》,相信可以通過對系列中的值類型與引用類型的3篇的理解,加深對參數傳遞之惑的昭雪。
了解了引用類型參數按值傳遞的實質,我們有必要再引入另一個參數傳遞的概念,那就是:按引用傳遞,通常稱為引用參數。這二者的本質區別可以小結為:
• 引用類型參數的按值傳遞,傳遞的是參數本身的值,也就是上面提到的對象的引用;
• 按引用傳遞,傳遞的不是參數本身的值,而是參數的地址。如果參數為值類型,則傳遞的是該值類型的地址;如果參數為引用類型,則傳遞的是對象引用的地址。
關于引用參數的詳細概念,我們馬上就展開來討論,不過還是先分析一下string類型的特殊性,究竟特殊在哪里?
關于string的討論,在本人拙作《第九回:品味類型---值類型與引用類型(中)-規則無邊》已經有了討論,也就是開篇陳述的本文成文的歷史,所以在上述分析的基礎上,我認為應該更能對第九回的問題,做以更正。
string本身為引用類型,因此從本文的分析中可知,對于形如
static void ShowInfo(string aStr){...}的傳遞形式,可以清楚的知道這是按值傳遞,也就是本文總結的引用類型參數的按值傳遞。因此,傳遞的是aStr對象的值,也就是aStr引用指針。接下來我們看看下面的示例來分析,為什么string類型在傳遞時表現出特殊性及其產生的原因?
Code
下面對上述示例的執行過程簡要分析一下:首先,string str = "Old String"產生了一個新的string對象,如圖表示:
然后執行ChangeStr(aStr),也就是進行引用類型參數的按值傳遞,我們強調說這里傳遞的是引用類型的引用值,也就是地址指針;然后調用ChangeStr方法,過程aStr = "Changing String"完成了以下的操作,先在新的一個地址生成一個string對象,該新對象的值為"Changing String",引用地址為0x06賦給參數aStr,因此會改變aStr的指向,但是并沒有改變原來方法外str的引用地址,執行過程可以表示為: