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

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

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

      那些年搞不懂的術語、概念:協變、逆變、不變體

      簡述什么是協變性、逆變性、不變性

      • 協變性,如:string->object (子類到父類的轉換)
      • 逆變性,如:object->string (父類到子類的轉換)
      • 不變性,基于上面兩種情況,不可變。具體下面再做分析。

      泛型委托的可變性

      先使用框架定義的泛型委托Func和Action做例子(不了解的請戳

      協變:(string->object)

      Func<string> func1 = () => "農碼一生";
      Func<object> func2 = func1;

      逆變:(object->string)

      Action<object> func3 = t => { };
      Action<string> func4 = func3;

      上面代碼沒有任何問題。

      接著我們自己定義委托試試:

      我X,看人不來哦。為什么自定義的委托卻不能協變呢。

      我看看系統定義的Func到底和我們自定義的有什么不同:

      public delegate TResult Func<out TResult>();

      多了一個out,什么鬼:

      • out:對于泛型類型參數,out 關鍵字指定該類型參數是協變的。 可以在泛型接口和委托中使用 out 關鍵字。來源
      • in:對于泛型類型參數,in 關鍵字指定該類型參數是逆變的。 可以在泛型接口和委托中使用 in 關鍵字。來源

      那么我們可以修改自定義委托:

      完美!

      那如果我們要實現逆變性呢:

      直接逆變是不可行的,我們需要修改泛型類型參數:

      我們發現整個委托參數都變了。本來的返回值,改成輸入參數才行。

      結論:

      • in->輸入參數->可逆變(父類到子類的轉變[如 object->string])
      • out->返回值->可協變(子類到父類的轉變[如 string->object])

       

      假設:如果泛型參數中既存在in又存在out改如何:

      delegate Tout MyFunc<in Tin, out Tout>(Tin obj);
      MyFunc<object, string> str1 = t => "農碼一生";
      MyFunc<string, string> str2 = str1;//第一個泛型的逆變(object->string)
      MyFunc<object, object> str3 = str1;//第二個泛型的協變(string->object)
      MyFunc<string, object> str4 = str1;//第一個泛型的逆變和第二個泛型的協變

      以上都是沒有問題的。 

      然后我們看看編譯后的C#代碼:

      結論:

      • 所謂的逆變其實只是編譯后進行了強制類型轉換而已。

      以上代碼也可以直接寫成:

      //delegate Tout MyFunc<in Tin, out Tout>(Tin obj);
      MyFunc<string, string> str5 = t => "農碼一生";
      MyFunc<object, object> str6 = t => "農碼一生";
      MyFunc<string, object> str7 = t => "農碼一生";

      泛型接口的可變性

      接著看框架默認接口:

      協變:(子類->父類)

      IEnumerable<string> list = new List<string>();
      IEnumerable<object> list2 = list;

      逆變:(父類-> 子類)

      IComparable<object> list3 = null;
      IComparable<string> list4 = list3;

      接下來我們試試自定泛型接口:

      首先定義測試類型、接口:

      //
      public class People
      { }
      //老師(繼承People[人])
      public class Teacher : People
      { }
      //運動
      public interface IMotion<T>
      { }
      //跑步
      public class Run<T> : IMotion<T>
      { }

      然后我們測試協變性:

      同樣我們需要把接口 interface IMotion<T> 定義為 interface IMotion<out T> 

      //運動
      public interface IMotion<out T>{}
      IMotion<Teacher> x = new Run<Teacher>();
      IMotion<People> y = x;

      如果我們要測試逆變性,則需要把 interface IMotion<T>  定義為 interface IMotion<in T> 

      //運動
      public interface IMotion<in T>{}
      IMotion<People> x2 = new Run<People>();
      IMotion<Teacher> y2 = x2;

      泛型接口的逆變,編譯后同樣進行了強制轉換:

      當然,我們也可以直接寫成:

      IMotion<Teacher> y3 = new Run<People>();

      不變性

      從上面我們知道逆變性的代碼編譯后都會進行強制轉換。假設:那我們不用out、in直接手動強制轉換是否可以?:

      //
      public class People { }
      //老師(繼承People[人])
      public class Teacher : People { }
      //運動
      public interface IMotion<T> { }
      //跑步
      public class Run<T> : IMotion<T> { }
      //協變
      IMotion<Teacher> x = new Run<Teacher>();
      IMotion<People> y = (IMotion<People>)x;
      
      //逆變
      IMotion<People> x2 = new Run<People>();
      IMotion<Teacher> y2 = (IMotion<Teacher>)x2;
      IMotion<Teacher> y3 = (IMotion<Teacher>)new Run<People>();

      天才的我發現編譯成功了,沒有任何問題!且還可以同時協變、逆變??不對,真的天才了嗎?我們運行試試:

      看來我還是太單純了,如果真的這么容易繞過去,Microsoft又何必去搞個out、in關鍵字。

      對于同一個泛型參數,我們既想有協變性又想逆變性,咋辦?答案是不可行。這就會出現第三種情況,既不可以協變又不可以逆變。稱為不變性。

      (我們在IMotion定義兩個方法)

      //運動
      public interface IMotion<T>
      {
          T Show();
          void Match(T t);
      }

      上面我們測試過,代碼直接強制轉換是不能實現協變、逆變的。那么我們只能通過out、in來實現。如果現在我們在泛型參數添加out或in屬性會如何?:

      我們發現out和in都不能用。在用out時,有個傳入參數為泛型 void Match(T t) 的方法。使用in時,有個返回參數為泛型 T Show() 的方法。現在就出現了是矛更鋒利還是盾更堅硬的問題了。

      最后結果是:都不能用,既不能協變,也不能逆變。此為不變體

      小知識:

      C#4.0之前 IEnumerable<T> 、 IComparable<T> 、 IQueryable<T> 等接口都不支持可變性,在4.0及之后才支持。因為4.0之前定義的泛型接口沒有添加out、in關鍵字,有興趣可以切換版本看看。

      延伸思考

      為什么in[輸入參數]就只能逆變?分析如下:

      //
      public class People { }
      //老師(繼承People[人])
      public class Teacher : People
      {
          //薪水
          public decimal Salary { get; set; }
      }
      
      //運動
      public interface IMotion<in T>
      {
          void Match(T t);
      }
      //跑步
      public class Run<T> : IMotion<T>
      {
          public void Match(T t)
          {
              //假設中間有很多邏輯.....       
          }
      }

      為什么out[返回值]只能協變?分析如下:

      //
      public class People { }
      //老師(繼承People[人])
      public class Teacher : People
      {
          //薪水
          public decimal Salary { get; set; }
      }
      
      //運動
      public interface IMotion<out T>
      {
          T Show();
          //void Match(T t);
      }
      //跑步
      public class Run<T> : IMotion<T>
      {
          public T Show()
          {
              return default(T);
          }
          //public void Match(T t)
          //{
          //    //假設中間有很多邏輯.....         
          //}
      }

      這里有兩個關鍵點:

      • 傳入參數(in)是把參數當成父類來用,顯然可以逆變(子類當成父類來用[里氏替換原則]),但是卻不可以把父類當子類來用(如:子類存在有而父類沒有的方法或屬性)
      • 返回值(out)返回值類型用父類來接收,顯然可以協變(父類可以接收一切子類),但卻不可用子類接收父類數據(如:父類代表的對象不能強制轉給子類[string str = (string)objcet])

      。。。是不是有點越想越頭暈,想不明白就慢慢想。自己動動手。

      如果實在想的頭大,就把它當成是烏龜的屁股(龜腚\規定)吧,知道是C#做的一種安全限制!

      總結

      關于泛型接口、泛型委托的可變性:

      • 協變 -> 比較和諧正常的變化 -> 子類轉父類 [如 string轉object] -> 必須有out標識 [返回值]
      • 逆變 -> 逆天的變化 -> 父類轉子類 [如object轉string] -> 必須有in標識 [傳入參數]  (父親變兒子,越活越年輕,還不夠逆天嗎?)
      • 所謂的逆變,會在編譯后的C#代碼中進行強制類型轉換。
      • 示例:
        • IEnumerable<string> list = new List<string>();  
          IEnumerable<object> list2 = list; //協變
          IEnumerable<object> list2 = new List<string>();  //(也可以直接寫成這樣)

        • IComparable<object> list3 = null;
          IComparable<string> list4 = list3; //逆變  編譯后 [ IComparable<string> list4 = (IComparable<string>) list3;]

      注意:

      • 不支持類的類型參數的可變性
      • 只有泛型接口和泛型委托可以擁有可變的類型參數(out、in)
      • 可變性只支持引用轉換。(不能用于值類型)
      • 類型參數使用了 out 或者 ref 將禁止可變性

       

      好了,今天就到這里。沒啥高深的技術知識,主要為理解協變、逆變、不變體等術語和概念。

      本文已同步至索引目錄:《C#基礎知識鞏固

       

      同類文章推薦:

      http://www.rzrgm.cn/haoyifei/p/5760959.html

      http://www.rzrgm.cn/LoveJenny/archive/2012/03/13/2392747.html

      http://www.rzrgm.cn/Ninputer/archive/2008/11/22/generic_covariant.html

       

      posted @ 2016-08-29 08:55  農碼一生  閱讀(20226)  評論(37)    收藏  舉報
      .
      主站蜘蛛池模板: 正在播放酒店约少妇高潮| 国产草草影院ccyycom| 黄色三级亚洲男人的天堂| 国产福利永久在线视频无毒不卡 | 国产日韩精品欧美一区灰| 欧美日本国产va高清cabal| 国产一区在线观看不卡| 亚洲av久久精品狠狠爱av| 青青国产揄拍视频| 日韩有码国产精品一区| 亚洲av永久无码精品水牛影视| 2021亚洲va在线va天堂va国产| 67194熟妇在线直接进入| 中文有码字幕日本第一页| 亚洲欧美成人久久综合中文网| 亚洲av综合色一区二区| 大又大又粗又硬又爽少妇毛片 | 亚洲国产精品综合久久20| 日韩欧美亚洲综合久久| 国产一区二区三区自拍视频 | 亚洲成人高清av在线| 精品国产成人午夜福利| 国产99视频精品免费视频36| 久久er99热精品一区二区| 日韩精品国产中文字幕| 又大又粗欧美成人网站| 亚洲日韩久热中文字幕| 粉嫩国产av一区二区三区| 国产亚洲色婷婷久久99精品| 国产日韩综合av在线| 亚洲男人天堂2018| av偷拍亚洲一区二区三区| 绿春县| 中文字幕国产日韩精品| 高清性欧美暴力猛交| 欧美成本人视频免费播放| 亚洲精品无码你懂的| 最新国产在线拍揄自揄视频| 丝袜美腿亚洲综合第一页| 中国老太婆video| 亚洲人成色99999在线观看|