亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

[C#]淺析ref、out參數

2019-11-17 02:18:03
字體:
來源:轉載
供稿:網友

[C#]淺析ref、out參數

按引用傳遞的參數算是C#與很多其他語言相比的一大特色,想要深入理解這一概念應該說不是一件容易的事,再把值類型和引用類型給參雜進來的話就變得更加讓人頭暈了。經??吹接腥税寻匆脗鬟f和引用類型混為一談,讓我有點不吐不快。再加上前兩天碰到的一個有意思的問題,讓我更加覺得應該整理整理關于ref和out的內容了。

一、什么是按引用傳遞

ref和out用起來還是非常簡單的,就是在普通的按值傳遞的參數前加個ref或者out就行,方法定義和調用的時候都得加。ref和out都是表示按引用傳遞,CLR也完全不區分ref還是out,所以下文就直接以ref為例來進行說明。

大家都知道,按值傳遞的參數在方法內部不管怎么改變,方法外的變量都不會受到影響,這從學C語言時候就聽老師說過的了。在C語言里想要寫一個Swap方法該怎么做?用指針咯。那么在C#里該怎么做?雖然也可以用指針,但是更通常也更安全的做法就是用ref咯。

說到這里,有一點需要明確,按值傳遞的參數到底會不會被改變。如果傳的是int參數,方法外的變量肯定是完完全全不變的咯,可是如果傳的是個List呢?方法內部對這個List的所有增刪改都會反映到方法外頭,方法外查一下Count就能看出來了是吧。那么傳List的這個情況,也代表了所有引用類型參數的情況,方法外的變量到底變沒變?不要聽信某些論調說什么“引用類型就是傳引用”,不用ref的情況下引用類型參數仍然傳的是“值”,所以方法外的變量仍然是不變的。

以上總結起來就是一句話:按值傳遞參數的方法永遠不可能改變方法外的變量,需要改變方法外的變量就必須按引用傳遞參數。

PS:不是通過傳參的方式傳入的變量當然是可以被改變的,本文不對這種情況做討論。

二、參數傳遞的是什么

按值傳參傳的就是值咯,按引用傳參傳的就是引用咯,這么簡單的問題還有啥可討論的呢??墒窍胍幌?,值類型變量和引用類型變量組合上按值傳參和按引用傳參,一共四種情況,某些情況下“值”和“引用”可能指的是同一個東西。

先簡單地從變量說起吧,一個變量總是和內存中的一個對象相關聯。對于值類型的變量,可以認為它總是包含兩個信息,一是引用,二是對象的值。前者即是指向后者的引用。對于引用類型的變量,可以認為它也包含兩個信息,一是引用,二是另一個引用。前者仍然是指向后者的引用,而后者則指向堆中的對象。

所謂的按值傳遞,就是傳遞的“二”;按引用傳遞,就是傳遞的“一”。也就是說,在按值傳遞一個引用類型的時候,傳遞的值的內容是一個引用。

大概情況類似于這樣:

按值傳遞時就像是這樣:

可以看到,不管方法內部對“值”和“B引用”作什么修改,兩個變量包含的信息是不會有任何變化的。但是也可以看到,方法內部是可以通過“B引用”對“引用類型對象”進行修改的,這就出現了前文所說的發生在List上的現象。而按引用傳遞時就像是這樣:

可以看到,這個時候方法內部是可以通過“引用”和“A引用”直接修改變量的信息的,甚至可能發生這樣的情況:

這個時候的方法實現可能是這樣的:

void SampleMethod(ref object obj){    //.....    obj = new object();    //.....}

三、從IL來看差異

接下來看一看IL是怎么對待按值或者按引用傳遞的參數。比如這一段C#代碼:

class Class{    void Method(Class @class) { }    void Method(ref Class @class) { }    // void Method(out Class @class) { }}

這一段代碼是可以正常通過編譯的,但是取消注釋就不行了,原因前面也提到了,IL是不區分ref和out的。也正是因為這一種重載的可能性,所以在調用方也必須寫明ref或out,不然編譯器沒法區分調用的是哪一個重載版本。Class類的IL是這樣的:

.class PRivate auto ansi beforefieldinit CsConsole.Class    extends [mscorlib]System.Object{    // Methods    .method private hidebysig static         void Method (            class CsConsole.Class 'class'        ) cil managed     {        // Method begins at RVA 0x20b4        // Code size 1 (0x1)        .maxstack 8        IL_0000: ret    } // end of method Class::Method    .method private hidebysig static         void Method (            class CsConsole.Class& 'class'        ) cil managed     {        // Method begins at RVA 0x20b6        // Code size 1 (0x1)        .maxstack 8        IL_0000: ret    } // end of method Class::Method} // end of class CsConsole.Class

為了閱讀方便,我把原有的默認無參構造函數去掉了??梢钥吹絻蓚€方法的IL僅僅只有一個&符號的差別,這一個符號的差別也是兩個方法可以同名的原因,因為它們的參數類型是不一樣的。out和ref參數的類型則是一樣的?,F在給代碼里加一點內容,讓差別變得更明顯一些:

class Class{    int i;    void Method(Class @class)    {        @class.i = 1;    }    void Method(ref Class @class)    {        @class.i = 1;    }}

現在的IL是這樣的:

.class private auto ansi beforefieldinit CsConsole.Class    extends [mscorlib]System.Object{    // Fields    .field private int32 i    // Methods    .method private hidebysig         instance void Method (            class CsConsole.Class 'class'        ) cil managed     {        // Method begins at RVA 0x20b4        // Code size 8 (0x8)        .maxstack 8        IL_0000: ldarg.1        IL_0001: ldc.i4.1        IL_0002: stfld int32 CsConsole.Class::i        IL_0007: ret    } // end of method Class::Method    .method private hidebysig         instance void Method (            class CsConsole.Class& 'class'        ) cil managed     {        // Method begins at RVA 0x20bd        // Code size 9 (0x9)        .maxstack 8        IL_0000: ldarg.1        IL_0001: ldind.ref        IL_0002: ldc.i4.1        IL_0003: stfld int32 CsConsole.Class::i        IL_0008: ret    } // end of method Class::Method} // end of class CsConsole.Class

帶ref的方法里多了一條指令“ldind.ref”,關于這條指令MSDN的解釋是這樣的:

將對象引用作為 O(對象引用)類型間接加載到計算堆棧上。

簡單來說就是從一個地址取了一個對象引用,這個對象引用與無ref版本的“arg.1”相同的,即按值傳入的@class。再來換一個角度看看,把代碼改成這樣:

class Class{    void Method(Class @class)    {        @class = new Class();    }    void Method(ref Class @class)    {        @class = new Class();    }}

IL是這樣的:

.class private auto ansi beforefieldinit CsConsole.Class    extends [mscorlib]System.Object{    // Methods    .method private hidebysig         instance void Method (            class CsConsole.Class 'class'        ) cil managed     {        // Method begins at RVA 0x20b4        // Code size 8 (0x8)        .maxstack 8        IL_0000: newobj instance void CsConsole.Class::.ctor()        IL_0005: starg.s 'class'        IL_0007: ret    } // end of method Class::Method    .method private hidebysig         instance void Method (            class CsConsole.Class& 'class'        ) cil managed     {        // Method begins at RVA 0x20bd        // Code size 8 (0x8)        .maxstack 8        IL_0000: ldarg.1        IL_0001: newobj instance void CsConsole.Class::.ctor()        IL_0006: stind.ref        IL_0007: ret    } // end of method Class::Method} // end of class CsConsole.Class

這一次兩方的差別就更大了。無ref版本做的事很簡單,new了一個Class對象然后直接賦給了@class。但是有ref版本則是先取了ref引用留著待會用,再new了Class,然后才把這個Class對象賦給ref引用指向的地方。在來看看調用方會有什么差異:

class Class{    void Method(Class @class) { }    void Method(ref Class @class) { }    void Caller()    {        Class @class = new Class();        Method(@class);        Method(ref @class);    }}

.method private hidebysig     instance void Caller () cil managed {    // Method begins at RVA 0x20b8    // Code size 22 (0x16)    .maxstack 2    .locals init (        [0] class CsConsole.Class 'class'    )    IL_0000: newobj instance void CsConsole.Class::.ctor()    IL_0005: stloc.0    IL_0006: ldarg.0    IL_0007: ldloc.0    IL_0008: call instance void CsConsole.Class::Method(class CsConsole.Class)    IL_000d: ldarg.0    IL_000e: ldloca.s 'class'    IL_0010: call instance void CsConsole.Class::Method(class CsConsole.Class&)    IL_0015: ret} // end of method Class::Caller

差別很清晰,前者從局部變量表取“值”,后者從局部變量表取“引用”。

四、引用與指針

說了這么久引用,再來看一看同樣可以用來寫Swap的指針。很顯然,ref參數和指針參數的類型是不一樣的,所以這么寫是可以通過編譯的:

unsafe struct Struct{    void Method(ref Struct @struct) { }    void Method(Struct* @struct) { }}

這兩個方法的IL非常有意思:

.class private sequential ansi sealed beforefieldinit CsConsole.Struct    extends [mscorlib]System.ValueType{    .pack 0    .size 1    // Methods    .method private hidebysig         instance void Method (            valuetype CsConsole.Struct& 'struct'        ) cil managed     {        // Method begins at RVA 0x2050        // Code size 1 (0x1)        .maxstack 8        IL_0000: ret    } // end of method Struct::Method    .method private hidebysig         instance void Method (            valuetype CsConsole.Struct* 'struct'        ) cil managed     {        // Method begins at RVA 0x2052        // Code size 1 (0x1)        .maxstack 8        IL_0000: ret    } // end of method Struct::Method} // end of class CsConsole.Struct

ref版本是用了取地址運算符(&)來標記,而指針版本用的是間接尋址運算符(*),含義也都很明顯,前者傳入的是一個變量的地址(即引用),后者傳入的是一個指針類型。更有意思的事情是這樣的:

unsafe struct Struct{    void Method(ref Struct @struct)    {        @struct = default(Struct);    }    void Method(Struct* @struct)    {        *@struct = default(Struct);    }}
.class private sequential ansi sealed beforefieldinit CsConsole.Struct    extends [mscorlib]System.ValueType{    .pack 0    .size 1    // Methods    .method private hidebysig         instance void Method (            valuetype CsConsole.Struct& 'struct'        ) cil managed     {        // Method begins at RVA 0x2050        // Code size 8 (0x8)        .maxst
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
精品国产老师黑色丝袜高跟鞋| 国产综合香蕉五月婷在线| 欧美激情在线观看视频| 成人国产精品久久久久久亚洲| 国产成人精品综合| 成人久久18免费网站图片| 神马国产精品影院av| 国产一区二区香蕉| 欧美天堂在线观看| 日韩一区二区av| 亚洲3p在线观看| 国精产品一区一区三区有限在线| 欧美黄色www| 91wwwcom在线观看| 国产精品扒开腿做爽爽爽的视频| 久久亚洲精品中文字幕冲田杏梨| 免费99精品国产自在在线| 超碰91人人草人人干| 国产精品久久久久久久午夜| 国产综合久久久久久| 欧美日本高清一区| 成人免费自拍视频| 精品久久久久久久久久久久| 中文字幕在线观看日韩| 黑人欧美xxxx| 国产一区二区三区久久精品| 日韩高清电影免费观看完整版| 成人性生交大片免费观看嘿嘿视频| 精品美女国产在线| 夜夜躁日日躁狠狠久久88av| 91精品在线国产| 欧美重口另类videos人妖| 久久人体大胆视频| 久久久人成影片一区二区三区| 亚洲精品成人免费| 日韩美女激情视频| 欧美裸体视频网站| 欧美极品欧美精品欧美视频| 久久国产天堂福利天堂| 亚洲人av在线影院| 欧美乱大交xxxxx另类电影| 欧美猛交ⅹxxx乱大交视频| 亚洲午夜未删减在线观看| 热久久视久久精品18亚洲精品| 美女久久久久久久久久久| 亚洲成人av中文字幕| 91精品国产精品| 亚洲jizzjizz日本少妇| 久久色免费在线视频| 啪一啪鲁一鲁2019在线视频| 色噜噜国产精品视频一区二区| 麻豆一区二区在线观看| 亚洲男人的天堂在线播放| 68精品国产免费久久久久久婷婷| 久久久久久网站| 综合国产在线视频| 91亚洲精品久久久| 人人澡人人澡人人看欧美| 97在线视频观看| 国产精品一区二区久久国产| 久久久久亚洲精品成人网小说| 国产一区二区三区四区福利| 亚洲精品中文字幕有码专区| 国产精品亚洲欧美导航| 亚洲精品色婷婷福利天堂| 一本色道久久88精品综合| 永久免费毛片在线播放不卡| 国语自产偷拍精品视频偷| 欧美性受xxx| 亚洲香蕉成视频在线观看| 欧美精品aaa| 亚洲综合在线中文字幕| 亚洲欧洲国产精品| 一区二区三区日韩在线| 激情久久av一区av二区av三区| 45www国产精品网站| 亚洲网站在线播放| 好吊成人免视频| 91在线播放国产| 日韩欧美在线中文字幕| 免费成人高清视频| 亚洲成人激情在线| 久热精品视频在线观看一区| 中文字幕亚洲一区在线观看| 亚洲高清av在线| 久久久国产精品视频| 欧美激情一区二区三区高清视频| 国内伊人久久久久久网站视频| 国产精品亚洲片夜色在线| 国产一区av在线| 欧美成人精品激情在线观看| 久久久91精品| 91精品久久久久久久久久| 国产成人aa精品一区在线播放| 日韩黄色高清视频| 欧美成年人网站| 91日韩在线播放| 国产在线观看精品一区二区三区| 亚洲综合中文字幕在线| 成人久久18免费网站图片| 九九热最新视频//这里只有精品| 一区二区三区精品99久久| 国产精品久久久久久久电影| 视频在线观看99| 中文字幕国产精品| 在线a欧美视频| 日韩中文字幕精品| 久久久人成影片一区二区三区| 日韩av一区在线观看| 成人久久18免费网站图片| 国产精品午夜国产小视频| 亚洲国产精品人人爽夜夜爽| 久久乐国产精品| 欧美野外猛男的大粗鳮| 日韩欧美精品网址| 97久久国产精品| 国产精品女人网站| 亚洲欧美在线看| 亚洲va欧美va国产综合久久| 欧美亚洲日本黄色| 热re99久久精品国产66热| 韩国欧美亚洲国产| 精品国产拍在线观看| 欧美日韩成人黄色| 亚洲天堂久久av| 成人欧美在线视频| 在线免费观看羞羞视频一区二区| 欧美成人免费观看| 亚洲图片欧美午夜| 国产99久久精品一区二区 夜夜躁日日躁| 色偷偷偷亚洲综合网另类| 亚洲**2019国产| 亚洲大胆人体视频| 日韩欧美在线观看视频| 亚洲色图激情小说| 国内精久久久久久久久久人| 亚洲综合大片69999| 成人黄色片在线| 国产精品福利在线观看网址| 亚洲精选在线观看| 91高清视频在线免费观看| 亚洲人成网站免费播放| 欧美日韩久久久久| 亚洲欧洲美洲在线综合| 66m—66摸成人免费视频| 日韩动漫免费观看电视剧高清| 精品久久久久久久久久| 中文字幕欧美日韩在线| 在线精品播放av| 午夜精品一区二区三区视频免费看| 亚洲成人aaa| 色偷偷亚洲男人天堂| 亚洲电影免费在线观看| 狠狠色狠狠色综合日日小说| 国产欧美日韩精品丝袜高跟鞋| 一区二区三区美女xx视频| 欧美电影在线免费观看网站| 国产精品美女免费视频| 欧美午夜电影在线| 国模精品一区二区三区色天香| 91久热免费在线视频| 国产欧美va欧美va香蕉在| 日韩在线播放av| 国产成人精品一区二区在线|