麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 編程 > C# > 正文

C#4.0新特性:協(xié)變與逆變中的編程思想

2023-05-20 20:23:34
字體:
供稿:網(wǎng)友

現(xiàn)在我們接著來談?wù)凜#4.0中一個重要的新特性:協(xié)變(Covariance)與逆變(Contravariance)。對于協(xié)變與逆變,大家肯定不會感到陌生,但是我相信有很多人不能很清晰地說出他們之間的區(qū)別。我希望通過這篇文章能夠讓讀者更加深刻的認識協(xié)變與逆變。但是也不排除另一種可能,那就是讀者這篇文章你對這兩個概念更加模糊。文章一些內(nèi)容僅代表個人觀點,如有不妥,還望指正。

1、兩個概念:強類型與弱類型

為了后面敘述方便,我現(xiàn)在這里自定義兩個概念:強類型和弱類型。在本篇文章中,強類型和弱類型指的是兩個具有直接或者間接繼承關(guān)系的兩個類。如果一個類是另一個類的直接或者間接基類,那么它為弱類型,直接或者間接子類為強類型。后續(xù)的介紹中會用到的兩個類Foo和Bar先定義在這里。Bar繼承自Foo。Foo是弱類型,而Bar則是強類型。

public class Foo { //Others Members... }

public class Bar:Foo { //Others Members... }

有了強類型和弱類型的概念,我們就可以這樣的定義協(xié)變和逆變:如果類型TBar是基于強類型Bar的類型(比如類型參數(shù)為Bar的泛型類型,或者是參數(shù)/返回值類型為Bar的委托),而類型TFoo是基于弱類型Foo的類型,協(xié)變就是將TBar類型的實例賦值給TFoo類型的變量,而逆變則是將TFoo類型的實例賦值給TBar類型的變量。

2、委托中的協(xié)變與逆變的使用

協(xié)變和逆變主要體現(xiàn)在兩個地方:接口和委托,先來看看在委托中如何使用協(xié)變和逆變。現(xiàn)在我們定義了如下一個表示無參函數(shù)的泛型委托Function,類型參數(shù)為函數(shù)返回值的類型。泛型參數(shù)之前添加了一個out關(guān)鍵字表示T是一個協(xié)變變體。那么在使用過程中,基于強類型的委托Fucntion實例就可以賦值給基于弱類型的委托Fucntion變量。

public delegate T Function<out T>();

class Program
         {
                   static void Main()
                   {
                            Function funcBar = new Function(GetInstance);
                            Function funcFoo = funcBar;
                            Foo foo = funcFoo();
                   }
                   static Bar GetInstance()
                   {
                            return new Bar();
                   }
          }

接下來介紹逆變委托的用法。下面定義了一個名稱為Operate的泛型委托,接受一個具有泛型參數(shù)類型的參數(shù)。在定義泛型參數(shù)前添加了in關(guān)鍵字,表示T是一個基于逆變的變體。由于使用了逆變,我們就可以將基于弱類型的委托Operate實例就可以賦值給基于強類型的委托Operate變量。

public delegate void Operate<in T>(T instance);

class Program
          {
                   static void Main()
                   {
                            Operate opFoo = new Operate(DoSth);
                            Operate opBar = opFoo;
                            opBar(new Bar());
                   }
                   static void DoSth(Foo foo)
                   { //Others... }
          }

 3、接口中的協(xié)變與逆變的使用

接下來我們同樣通過一個簡單的例子來說明在接口中如何使用協(xié)變和逆變。下面定義了一個繼承自 IEnumerable接口的IGroup集合類型,和上面一樣,泛型參數(shù)T之前的out關(guān)鍵字表明這是一個協(xié)變。既然是協(xié)變,我們就可以將一個基于強類型的委托IGroup實例就可以賦值給基于弱類型的委托IGroup變量。

public interface IGroup<out T> : IEnumerable
        { }
        public class Group : List, IGroup
         { }
         public delegate void Operate<in T>(T instance); 
        class Program 
        {
                 static void Main() 
                {
                         IGroup groupOfBar = new Group();
                         IGroup groupOfFoo = groupOfBar;
                         //Others...
                 }
         }

下面是一個逆變接口的例子。首先定義了一個IPaintable的接口,里面定義了一個可讀寫的Color屬性,便是實現(xiàn)該接口的類型的對象具有自己的顏色,并可以改變顏色。類型Car實現(xiàn)了該接口。接口IBrush定義了一把刷子,泛型類型需要實現(xiàn)IPaintable接口,in關(guān)鍵字表明這是一個逆變。方法Paint用于將指定的對象粉刷成相應(yīng)的顏色,表示被粉刷的對象的類型為泛型參數(shù)類型。Brush實現(xiàn)了該接口。由于IBrush定義成逆變,我們就可以將基于強類型的委托IBrush實例就可以賦值給基于弱類型的委托IBrush變量。

public interface IPaintable 
        {
                 Color Color
                 {
                         get;
                         set;
                 }
         }
         public class Car : IPaintable
         {
                 public Color Color { get; set; }
          }
         public interface IBrush<in T> where T : IPaintable
         {
                 void Paint(T objectToPaint, Color color);
         }
         public class Brush : IBrush where T : IPaintable 
        {
                 public void Paint(T objectToPaint, Color color)
                 {
                         objectToPaint.Color = color; 
                }
         }
         class Program 
        {
                 static void Main() 
                {
                         IBrush brush = new Brush();
                         IBrush carBrush = brush;
                         Car car = new Car(); 
                        carBrush.Paint(car, Color.Red);
                         Console.WriteLine(car.Color.Name); 
                }
         }

4、從Func看協(xié)變與逆變的本質(zhì)

接下來我們來談?wù)剠f(xié)變和逆變的本質(zhì)區(qū)別是什么。在這里我們以我們非常熟悉的一個委托Func作為例子,下面給出了該委托的定義。我們可以看到Func定義的兩個泛型參數(shù)分別屬于逆變和協(xié)變。具體來說輸入?yún)?shù)類型為逆變,返回值類型為協(xié)變。

public delegate TResult Func<in T, out TResult>(T arg);

再重申以下這句話“輸入?yún)?shù)類型為逆變,返回值類型為協(xié)變”。然后,你再想想為什么逆變用in關(guān)鍵字,而協(xié)變用out關(guān)鍵字。這兩個不是偶然,實際上我們可以將協(xié)變/逆變與輸出/輸入匹配起來。

我們再從另一個角度來理解協(xié)變與逆變。我們知道接口代表一種契約,當一個類型實現(xiàn)一個接口的時候就相當于簽署了這份契約,所以必須是實現(xiàn)接口中所有的成員。實際上類型繼承也屬于一種契約關(guān)系,基類定義契約,子類“簽署”該契約。對于類型系統(tǒng)來說,接口實現(xiàn)和類型繼承本質(zhì)上是一致的。契約是弱類型,簽署這份契約的是強類型。

將契約的觀點應(yīng)用在委托上面,委托實際上定義了一個方法的簽名(參數(shù)列表和返回值),那么參數(shù)和返回值的類型就是契約,現(xiàn)在的關(guān)鍵是誰去履行這份契約。所有參數(shù)是外界傳入的,所以基于參數(shù)的契約履行者來源于外部,也就是被賦值變量的類型,所以被賦值變量類型是強類型。而對于代理本身來說,參數(shù)是一種輸入,也就是一種采用in關(guān)鍵字表示的逆變。

而對于委托的返回值,這是給外部服務(wù)的,是委托自身對外界的一種承諾,所以它自己是契約的履行著,因此它自己應(yīng)該是強類型。相應(yīng)地,對于代理本身來說,返回值是一種輸出,也就是一種采用out關(guān)鍵字定義的協(xié)變。

也正式因為這個原因,對于一個委托,你不能將參數(shù)類型定義成成協(xié)變,也不能將返回類型定義成逆變。下面兩中變體定義方式都是不能通過編譯的。

delegate TResult Fucntion<out T, TResult>(T arg);

delegate TResult Fucntionin TResult>(T arg);

說到這里,我想有人要問一個問題,既然輸入表示逆變,輸出表示協(xié)變,委托的輸出參數(shù)應(yīng)該定義成協(xié)變了?非也,實際上輸出參數(shù)在這里既輸出輸出,也輸出輸入(畢竟調(diào)用的時候需要指定一個對應(yīng)類型的對象)。也正是為此,輸出參數(shù)的類型及不能定義成協(xié)變,也不能定義成逆變。所以下面兩種變體的定義也是不能通過編譯的。

delegate void Action<in T>(out T arg);

delegate void Action<out T>(out T arg);

雖然這里指介紹了關(guān)于委托的協(xié)變與逆變,上面提到的契約和輸入/輸出的關(guān)系也同樣適用于基于接口的協(xié)變與逆變。你自己可以采用這樣的方式去分析上面一部分我們定義的IGroup和IBrush。

5、逆變實現(xiàn)了“算法”的重用

實際上關(guān)系協(xié)變和逆變體現(xiàn)出來的編程思想,還有一種我比較推崇的說法,那就是:協(xié)變是繼承的體現(xiàn),而逆變體現(xiàn)的則是多態(tài)。實際上這與上面分析的契約關(guān)系本質(zhì)上是一致的。

關(guān)于逆變,在這里請容我再啰嗦一句:逆變背后蘊藏的編程思想體現(xiàn)出了對算法的重用——我們?yōu)榛惗x了一套操作,可以自動應(yīng)用于所有子類的對象。

上一篇:C#的前途如何

下一篇:C#,自然的進步

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 久久国语对白 | 91成人影库 | 亚洲网站免费看 | 久久亚洲激情 | 蜜桃视频日韩 | 中文字幕一二三区芒果 | 性猛交ⅹxxx乱巴西 欧美日韩1区2区3区 | 国产成年人视频 | 久久精品a一级国产免视看成人 | 亚洲视频成人 | 成人国产视频在线观看 | 亚洲伊人色欲综合网 | 新久草视频 | 鲁丝一区二区二区四区 | 一级黄色片在线看 | 久久亚洲春色中文字幕久久 | 国产高清美女一级毛片久久 | xxxx hd video 69| 国产手机av在线 | 91网址在线播放 | 快播av在线| 久久精品视频日本 | 日本在线播放一区二区三区 | 久久久久.com| 亚洲一区免费观看 | 国产一区国产二区在线观看 | 久久亚洲精品久久国产一区二区 | 国产精品啪一品二区三区粉嫩 | 狠狠操天天射 | 欧美大胆xxxx肉体摄影 | 精品一区二区三区免费视频 | 欧美大胆xxxx肉体摄影 | 羞羞视频免费观看入口 | 蜜桃网在线| 黄色网址在线播放 | 一区二区久久久久草草 | 福利在线国产 | 成人不卡在线观看 | 亚洲一区二区三区在线免费观看 | 国产91在线免费 | 国产精品一区在线看 |