自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

開發(fā) 后端
現(xiàn)在我們接著來談?wù)凜#4.0中一個(gè)重要的新特性:協(xié)變(Covariance)與逆變(Contravariance)。對(duì)于協(xié)變與逆變,大家肯定不會(huì)感到陌生,但是我相信有很多人不能很清晰地說出他們之間的區(qū)別。我希望通過這篇文章能夠讓讀者更加深刻的認(rèn)識(shí)協(xié)變與逆變。

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

一、兩個(gè)概念:強(qiáng)類型與弱類型

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

  1. public class Foo     
  2.  {     
  3.      //Others Members...     
  4.  }     
  5.  public class Bar:Foo     
  6.  {     
  7.      //Others Members...     
  8.  } 

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

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

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

  1. public delegate T Function<out T>();     
  2.  class Program     
  3.  {     
  4.      static void Main()     
  5.     {     
  6.         Function funcBar = new Function(GetInstance);     
  7.         Function funcFoo = funcBar;    
  8.         Foo foo = funcFoo();     
  9.     }    
  10.      static Bar GetInstance()    
  11.      {    
  12.          return new Bar();    
  13.      }    
  14.  } 

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

  1. public delegate void Operate<in T>(T instance);   
  2. class Program   
  3. {   
  4. static void Main()   
  5. {   
  6. Operate opFoo = new Operate(DoSth);   
  7. Operate opBar = opFoo;   
  8. opBar(new Bar());   
  9. }   
  10. static void DoSth(Foo foo)   
  11. {   
  12. //Others...   
  13. }   

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

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

  1. public interface IGroup<out T> : IEnumerable  
  2. { }   
  3. public class Group : List, IGroup   
  4. { }   
  5. public delegate void Operate<in T>(T instance);   
  6. class Program   
  7. {   
  8. static void Main()   
  9. {   
  10. IGroup groupOfBar = new Group();   
  11. IGroup groupOfFoo = groupOfBar;   
  12. //Others...   
  13. }  
  14. }  

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

  1. public interface IPaintable   
  2. {  
  3. Color Color { getset; }   
  4. }  
  5. public class Car : IPaintable   
  6. {   
  7. public Color Color { getset; }   
  8. }   
  9.  
  10. public interface IBrush<in T> where T : IPaintable   
  11. {   
  12. void Paint(T objectToPaint, Color color);   
  13. }   
  14. public class Brush : IBrush where T : IPaintable   
  15. {   
  16. public void Paint(T objectToPaint, Color color)   
  17. {   
  18. objectToPaint.Color = color;   
  19. }   
  20. }   
  21.  
  22. class Program   
  23. {   
  24. static void Main()   
  25. {   
  26. IBrush brush = new Brush();   
  27. IBrush carBrush = brush;   
  28. Car car = new Car();   
  29. carBrush.Paint(car, Color.Red);   
  30. Console.WriteLine(car.Color.Name);   
  31. }   
  32. }  

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

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

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

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

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

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

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

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

  1. delegate TResult Fucntion<out T, TResult>(T arg);   
  2. delegate TResult Fucntionin TResult>(T arg);  

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

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

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

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

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

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

原文鏈接:http://www.cnblogs.com/artech/archive/2011/01/13/variance.html

【編輯推薦】

  1. 精通C#與.NET 4.0數(shù)據(jù)庫(kù)開發(fā):基礎(chǔ)、數(shù)據(jù)庫(kù)核心技術(shù)、項(xiàng)目實(shí)戰(zhàn)
  2. C#應(yīng)用Attribute特性 代碼統(tǒng)計(jì)分析
  3. C# 4.0權(quán)威指南
責(zé)任編輯:陳貽新 來源: 博客園
相關(guān)推薦

2009-08-03 18:24:28

C# 4.0協(xié)變和逆變

2009-05-27 11:30:20

C#Visual Stud協(xié)變

2012-03-13 09:32:15

C#協(xié)變

2009-08-19 16:51:14

C# 4.0 dyna

2009-06-03 14:50:17

C# 4.0泛型協(xié)變性

2022-04-18 20:12:03

TypeScript靜態(tài)類型JavaScrip

2009-05-26 09:28:22

C# 4.0dynamic動(dòng)態(tài)類型

2020-08-03 08:13:51

Vue3TypeScript

2009-05-26 11:15:31

C# 4.0dynamicVisual Stud

2009-08-13 09:46:49

C#歷史C# 4.0新特性

2010-05-25 08:34:10

C# 4.0

2009-10-20 15:03:29

ExpandoObje

2010-08-17 09:57:39

C#

2013-10-31 09:36:43

程序員程序高手

2009-08-26 17:10:09

C# 3.5新特性

2020-09-29 06:37:30

Java泛型

2009-02-03 09:33:26

動(dòng)態(tài)類型動(dòng)態(tài)編程C# 4.0

2009-08-10 18:16:33

ICustomQuer.NET 4.0

2009-08-31 14:45:07

Visual C# 3

2009-07-06 11:00:56

.NET 4.0新特性.NET
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)