一句話清晰總結協變(covariant)和逆變 (contravariant)
看到過園子里面幾篇協變和逆變的文章,但是總覺得寫得不夠清晰,文章這東西最重要的是要把自己想表達的觀點表達出來,這個過程應該是把復雜的東西消化出來從而簡單化,清晰化,而不是故弄玄虛,反其道而行之,下面我們言歸正傳啦。
我們先來看一段MSDN原文給協變,逆變和變體下個定義:
A generic interface or delegate is called variant if its generic parameters are declared covariant or contravariant. Both C# and Visual Basic enable you to create your own variant interfaces and delegates.
如果泛型接口或委托的泛型參數聲明為協變或逆變,則將該泛型接口或委托稱為“變體”。 C# 和 Visual Basic 都允許您創建自己的變體接口和委托。
通俗解釋:
變體定義 - 帶有協變或逆變參數的泛型接口或委托。也就是說協變和逆變主要關注點在泛型接口或委托。
那什么又是協變和逆變呢?
協變
我們先來看下面一個來自MSDN的例子:
// 協變 IEnumerable<string> strings = new List<string>(); IEnumerable<object> objects = strings; //大家看到了么一個聲明為IEnumerable<string>的接口類型被賦給了一個更低級別的IEnumerable<object>. //對,這就是協變。再來看一個例子: class Base { public static void PrintBases(IEnumerable<Base> bases) { foreach(Base b in bases) { Console.WriteLine(b); } } } class Derived : Base { public static void Main() { List<Derived> dlist = new List<Derived>(); Derived.PrintBases(dlist); //由于IEnumerable<T>接口是協變的,所以PrintBases(IEnumerable<Base> bases) //可以接收一個更加具體化的IEnumerable<Derived>作為其參數。 IEnumerable<Base> bIEnum = dlist; } }
下面給協變下個定義:
協變:讓一個帶有協變參數的泛型接口(或委托)可以接收類型更加精細化,具體化的泛型接口(或委托)作為參數,可以看成OO中多態的一個延伸。
逆變
// Assume that the following method is in the class:
// static void SetObject(object o) { }
Action<object> actObject = SetObject;
Action<string> actString = actObject;
string strHello(“Hello”);
actString(strHello);
大家看到了么?一個聲明為Action<object>的類型被賦給了一個Action<string>,大家都知道,Action<T>接收參數,沒有返回值,所以其中的object和string是其參數,這個過程其實就是參數的約束更加強了,也就是說讓參數類型更加精細化。下面我們來給逆變下個定義:
逆變:讓一個帶有協變參數的泛型接口(或委托)可以接收粒度更粗的泛型接口或委托作為參數,這個過程實際上是參數類型更加精細化的過程。
總結
一句話總結:協變讓一個粗粒度接口(或委托)可以接收一個更加具體的接口(或委托)作為參數(或返回值);逆變讓一個接口(或委托)的參數類型(或返回值)類型更加具體化,也就是參數類型更強,更明確。
通常,協變類型參數可用作委托的返回類型,而逆變類型參數可用作參數類型。對于接口,協變類型參數可用作接口的方法的返回類型,而逆變類型參數可用作接口的方法的參數類型。