<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      把 ref 和 out 關鍵字說透

      如果排版不好,可以到我個人博客上看,我是現在個人博客上寫的,歡迎捧場^^:http://www.dozer.cc

       

      ref 和 out 的區別

      網上有很多這方面的文章,但是大部分人總是糾結于他們在原理上的那一點點細微的區別,所以導致了難以區分它們,也不知道什么時候改用哪一個了。

      但是如果從試用場景的角度對它們進行區別的話,以后你一定不會再糾結了。

      當你明白它們的適用場景后,再去扣其中的原理,使用中的一些問題也就迎刃而解了~

       

      簡單的來說,它們的區別在于:

      ref 關鍵字 是作用是把一個變量的引用傳入函數,和 C/C++ 中的指針幾乎一樣,就是傳入了這個變量的棧指針。

      out 關鍵字 的作用是當你需要返回多個變量的時候,可以把一個變量加上 out 關鍵字,并在函數內對它賦值,以實現返回多個變量。

       

       

      幾個簡單的演示

      上面說了 ref 和 out 的作用,非常簡單,但是在具體使用的時候卻遇到了很多麻煩,因為 C# 中本身就區分了引用類型和值類型。

      我先舉幾個例子,來看看會出現哪些詭異的情況

       

      代碼段一:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      static void Main(string[] args)
      {
          int a;
          Test1(out a);//編譯通過
       
          int b;
          Test2(ref b);//編譯失敗
      }
       
      static void Test1(out int a)
      {
          a = 1;
      }
      static void Test2(ref int b)
      {
          b = 1;
      }

      這兩個關鍵字看起來用法一樣,為什么會有合格現象?

      網上的答案很簡單:out 關鍵字在傳入前可以不賦值,ref 關鍵字在傳入前一定要賦值。

      這是什么解釋?受之于魚但并沒有授之予漁!這到底是為什么呢?

      想知道背后真正原理的呢,就繼續看下去吧,后面我講會講到這里的區別。

       

      代碼二:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      static void Main(string[] args)
      {
          object a = new object(), b = new object(), c = new object();
       
          Test1(out a);
          Test2(ref b);
          Test3(c);
          //最終 a,b,c 分別是什么?
          //a,b = null
          //c 還是 object
      }
       
      static void Test1(out object a)
      {
          a = null;
      }
      static void Test2(ref object b)
      {
          b = null;
      }
      static void Test3(object c)
      {
          c = null;
      }

      新建三個 object,object是引用類型;三個函數,分別是 out,ref和普通調用;執行了一樣的語句;最后的結果為什么是這樣呢?

      如果你只是從淺層次理解了 out 和 ref 的區別,這個問題你一定回答不上了。(我以前也不知道)

      所以,這是為什么呢?繼續往下看。

       

      ^_^ 相信很多人暈了,我的目的達到了。(邪惡的笑~~)

      那么,下面,我為大家從兩個角度來分析一下。

      對于值類型來說,加 out、加 ref 和什么都不加有什么共同點和區別?

      對于引用類型來說,加 out、加 ref 和什么都不加有什么共同點和區別?

       

       

      問題一:關于值類型

      普通的傳遞值類型很簡單了,傳的只是一個值,沒難度,平時都是這么用的,很好區分,所以這里就不慘和進去了。

      接下來是 ref 和 out 的區別,為什么要了解區別呢?當然是為了了解怎么用它們,簡單的來說就是需要了解:什么時候該用哪個。

       

      個人總結有幾個原則:

      如果你是為了能多返回一個變量,那么就應該用 out:

      用 out 關鍵字有幾個好處:可以不關心函數外是否被賦值,并且如果在函數內沒有賦值的話就會編譯不通過。(提醒你一定要返回)

      你可以把它當成是另一種形式的 return 來用,我們來做一個類比:

      return 語句的特點:接收 return 的變量事先不需要賦值(當然如果賦值了也沒關系),在函數內必須 return。

      可以看到 out 關鍵字的作用、行為、特點 和 return 是完全一樣的。因為它當初設計的目的就是為了解決這個問題的。

       

      如果你想要像引用類型那樣調用值類型,那你就可以 ref:

      傳入值類型的引用后,你可以用它,也可以不用它,你也可以重新修改它的各個屬性,而函數外也可以隨之改變。

      我們來把 “傳值類型的引用” 和 “傳引用類型” 來做一個類比:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      static void Main(string[] args)
      {
          int a;
          Test1(ref a);//錯誤   1   使用了未賦值的局部變量“a”
       
          object b;
          Test2(b);//錯誤   2   使用了未賦值的局部變量“b”
      }
      static void Test1(ref int a) { }
       
      static void Test2(object b) { }

      傳入加了 ref 的值類型 和 傳入一個引用類型 的作用、行為、特點都是類似的。

      同樣,他們同時要遵守一個原則:傳入前必須賦值,這個是為什么呢?

      如果賦值后,傳入兩個函數的分別是 int a 的指針 和 object b 的指針。

      而不賦值的話,a 和 b 根本還不存在,那它們又怎么會有地址呢?

       

      注意:如果你只寫了 object a ,而在后面的代碼中沒有賦值,它并沒有真正地分配內存。

      我們可以看一下三個操作的 IL 代碼:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      private static void Main(string[] args)
      {
          //IL_0000: nop
          object a;//沒做任何事
       
          //IL_0002: ldnull
          //IL_0003: stloc.1
          object b = null;//在棧中增加了一個指針,指向 null
       
          //IL_0004: newobj instance void [mscorlib]System.Object::.ctor()
          //IL_0009: stloc.2
          object c = new object();//在棧中增加了一個指針,指向新建的 object 對象
      }

      傳入引用類型的目的是把一個已經存在的對象的地址傳過去,而如果你只是進行了 object a 聲明,并沒做復制,這行代碼跟沒做任何事!

      所以,除非你使用了 out 關鍵字,在不用關鍵字和用 ref 關鍵字的情況下,你都必須事先復制。 out 只是一種特殊的 return

       

      總結:

      現在你是否明白,當變量什么情況下該用什么關鍵字了嗎?其實有時候 ref 和 out 都可以達到目的,你需要根據你的初衷,和它們的特點,來衡量一下到底使用哪個了!

      另外,我們來看看兩個同樣的函數,用 out 和 ref 時候的 IL 代碼

      原函數:

      1
      2
      3
      4
      5
      6
      7
      8
      private static void Test1(out int a)
      {
          a = 1;
      }
      private static void Test2(ref int a)
      {
          a = 1;
      }

      IL代碼:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      .method private hidebysig static
          void Test1 (
              [out] int32& a
          ) cil managed
      {
          // Method begins at RVA 0x2053
          // Code size 5 (0x5)
          .maxstack 8
       
          IL_0000: nop
          IL_0001: ldarg.0
          IL_0002: ldc.i4.1
          IL_0003: stind.i4
          IL_0004: ret
      } // end of method Program::Test1
       
      .method private hidebysig static
          void Test2 (
              int32& a
          ) cil managed
      {
          // Method begins at RVA 0x2059
          // Code size 5 (0x5)
          .maxstack 8
       
          IL_0000: nop
          IL_0001: ldarg.0
          IL_0002: ldc.i4.1
          IL_0003: stind.i4
          IL_0004: ret
      } // end of method Program::Test2

      發現了嗎? 它們在函數內部完全是一樣的!因為他們的原理都是傳入了這個變量的引用。只是 out 關鍵字前面出現了一個標記 [out]

      它們在原理上的區別主要在于編譯器對它們進行了一定的限制。

      最上面“代碼段一”中的問題你現在明白了嗎?

       

       

      問題二:關于引用類型

      對于值類型來說,最難區別的是 ref 和 out,而對于引用類型來說就不同了。

      首先,引用類型傳的是引用,加了 ref 以后也是引用,所以它們是一樣的?暫時我們就這么認為吧~ 我們暫時認為它們是一樣的,并統稱為:傳引用。

      所以,對于引用類型來說,out 和 傳引用 的區別跟對于值類型傳 ref 和 out 的區別類似,具體適用場景也和值類型類似,所以就不多加闡述了。

       

      雖然我們說直接傳和加 ref 都可以統稱為傳引用,但是它們還是有區別的!而且是一個很隱蔽的區別。

      我們再來看一下最上面的代碼段二:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      static void Main(string[] args)
      {
          object a = new object(), b = new object(), c = new object();
       
          Test1(out a);
          Test2(ref b);
          Test3(c);
          //最終 a,b,c 分別是什么?
          //a,b = null
          //c 還是 object
      }
       
      static void Test1(out object a)
      {
          a = null;
      }
      static void Test2(ref object b)
      {
          b = null;
      }
      static void Test3(object c)
      {
          c = null;
      }

      out 關鍵字就相當于 return ,所以內部賦值為 null ,就相當于 return 了 null

      可是,為什么引用類型還要加 ref 呢?它本身部已經是引用了嗎?為什么加了以后就會有天大的區別呢?!

       

      用一句話概括就是:不加 ref 的引用是堆引用,而加了 ref 后就是棧引用! @_@ 好搞啊。。什么跟什么?讓我們一步步說清楚吧!

       

      正常的傳遞引用類型:

      加了 ref 的傳遞引用類型:

      這兩張圖對于上面那句話的解釋很清楚了吧?

      如果直接傳,只是分配了一個新的棧空間,存放著同一個地址,指向同一個對象。

      內外指向的都是同一個對象,所以對 對象內部的操作 都是同步的。

      但是,如果把函數內部的 obj2 賦值了 null,只是修改了 obj2 的引用,而 obj1 依然是引用了原來的對象。

      所以上面的例子中,外部的變量并沒有收到影響。

      同樣,如果內部的對象作了  obj2 = new object() 操作以后,也不會對外部的對象產生任何影響!

       

      而加了 ref  后,傳入的不是 object 地址,傳入的是 object 地址的地址!

      所以,當你對 obj2 賦 null 值的時候,其實是修改了 obj1 的地址,而自身的地址沒變,還是引用到了 obj1

       

      雖然在函數內部的語句是一樣的,其實內部機制完全不同。我們可以看一下IL代碼,一看就知道了!

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      .method private hidebysig static
          void Test1 (
              object a
          ) cil managed
      {
          // Method begins at RVA 0x2053
          // Code size 5 (0x5)
          .maxstack 8
       
          IL_0000: nop
          IL_0001: ldnull
          IL_0002: starg.s a
          IL_0004: ret
      } // end of method Program::Test1
       
      .method private hidebysig static
          void Test2 (
              object& a
          ) cil managed
      {
          // Method begins at RVA 0x2059
          // Code size 5 (0x5)
          .maxstack 8
       
          IL_0000: nop
          IL_0001: ldarg.0//多了這行代碼
          IL_0002: ldnull
          IL_0003: stind.ref
          IL_0004: ret
      } // end of method Program::Test2

      上面是直接傳入,并賦 null 值的

      下面是加 ref 的

      我們可以發現僅僅是多了一行代碼:IL_0001: ldarg.0

      其實,這樣代碼的作用就是講參數0加載到堆棧上,也就是先根據引用,找到了外部的變量,然后再根據外部的變量,找到了最終的對象!

       

      那現在你知道什么時候該加 ref,什么時候不用加 ref 了嗎?

      再看了一個例子:

      1
      2
      3
      4
      5
      6
      7
      8
      private static void Test1(List<int> list)
      {
          list.Clear();
      }
      private static void Test2(ref List<int> list)
      {
          list = new List<int>();
      }

      同樣是清空一個 List,如果沒加 ref ,只能用 clear。

      而加了 ref 后可以直接 new 一個新的~

      如果你沒加 ref 就直接 new 一個新的了,抱歉,外部根本不知道有這個東西,你們操作的將不是同一個 List

       

      所以,你一定要了解這點,并注意一下幾件事:

      1、一般情況下不要用 ref

      2、如果你沒加 ref,千萬別直接給它賦值,因為外面會接收不到…

       

       

      現在你全部明白了嗎?^_^

       

      原文地址:把 ref 和 out 關鍵字說透

      posted @ 2011-10-28 17:03  Dozer  閱讀(3649)  評論(14)    收藏  舉報
      主站蜘蛛池模板: 亚洲国产欧美在线人成大黄瓜| 色熟妇人妻久久中文字幕| 1000部拍拍拍18勿入免费视频| 康乐县| 国内精品卡一卡二卡三| 亚洲欧洲日产国无高清码图片| 国产亚洲欧美精品久久久| 欧美成人午夜在线观看视频| 国产精品污双胞胎在线观看| 亚洲天堂av日韩精品| 一边添奶一边添p好爽视频| 一本色道久久—综合亚洲| 日韩精品中文字幕一线不卡| 欧美奶涨边摸边做爰视频| 国内极度色诱视频网站| 日韩加勒比一本无码精品| 免费人成网站免费看视频| 欧美精品人人做人人爱视频| 一区二区亚洲精品国产精| 久久精品国产再热青青青| 亚洲美免无码中文字幕在线| 乱码中文字幕| 无码人妻丰满熟妇区毛片18| 午夜福利精品国产二区| 亚洲午夜精品国产电影在线观看| 国产肉丝袜在线观看| 亚洲精品漫画一二三区| 亚洲国产精品综合久久20| 国产精品高潮无码毛片| 中国CHINA体内裑精亚洲日本| 国产免费视频一区二区| 国产午夜精品一区理论片| 男人又大又硬又粗视频| 资源在线观看视频一区二区| 欧美孕妇乳喷奶水在线观看| 平定县| 国产 麻豆 日韩 欧美 久久| 国产L精品国产亚洲区在线观看| 长腿校花无力呻吟娇喘| 韩国午夜福利片在线观看| 日本高清日本在线免费|