前言
工作一年了,平時(shí)也喜歡看看書(shū),逛逛園子;但說(shuō)到寫(xiě)博,還真的沒(méi)有,說(shuō)到底,只有一個(gè)字:懶!現(xiàn)在想改掉這個(gè)“毛病”了,希望多把平時(shí)工作學(xué)習(xí)到的知識(shí)和遇到的問(wèn)題記錄下來(lái),一是可以梳理自己的思路,加深理解;二是可以向更多的朋友學(xué)習(xí)和分享;三是可以鍛煉自己的寫(xiě)作水平;可謂百利而無(wú)一害!
平時(shí)偶爾會(huì)遇到一些小問(wèn)題,很多時(shí)候都是查了記住,或者簡(jiǎn)單寫(xiě)寫(xiě)筆記,當(dāng)時(shí)理解就過(guò)了,沒(méi)有形成文檔,等過(guò)段時(shí)間又遇到同樣的問(wèn)題,又要重新去查去理解,甚是麻煩。希望以后把這些東西寫(xiě)成文章,盡管可能是很小的問(wèn)題,也當(dāng)做筆記記錄。關(guān)于c#的一些概念、語(yǔ)法或者規(guī)范,就記錄在【c#筆記】。由于不是初學(xué)邊學(xué)邊記,所以沒(méi)有一定的時(shí)間和學(xué)習(xí)順序,只是平時(shí)遇到覺(jué)得有必要,就記錄下來(lái)。
一、遇到問(wèn)題
工作是基于.net3.5開(kāi)發(fā),實(shí)際過(guò)程遇到一個(gè)問(wèn)題。假設(shè)我們有一個(gè) Base 類(lèi),一個(gè) Derived 類(lèi),Derived 繼承了 Base。如下:
class Base { } class Derived : Base { }
當(dāng)我用IEnumerable<Base> 作為形參,List<Derived> 作為實(shí)參時(shí),發(fā)現(xiàn)編譯出錯(cuò)了!原本父類(lèi)作為形參,傳遞子類(lèi)是再正常不過(guò)的,但在泛型中確編譯不通過(guò)。
二、探究問(wèn)題
通常我們?cè)谠O(shè)計(jì)參數(shù)和返回值都有一個(gè)原則,參數(shù)要盡可能“泛”,返回值要盡可能的“細(xì)”。泛,指得是用接口或者父類(lèi)作為參數(shù),這樣可以接收更多的參數(shù)類(lèi)型;細(xì),指的是返回具體類(lèi)型,這樣可以更好說(shuō)明方法的作用。
舉個(gè)例子:
string[] strs = new string[] { "hello", "Word" }; //這樣的缺點(diǎn)是數(shù)組就傳遞不了了,還要調(diào)用一次 ToList() static void Test_1(List<string> list) { } //正確的做法,應(yīng)該用IEnumerable<T> static void Test_2(IEnumerable<string> list) { }
可見(jiàn),參數(shù)的“泛”可以提供更大的靈活性。
接著就進(jìn)入本次的主題:抗變與協(xié)變。需要說(shuō)明的是,抗變與協(xié)變是在4.0開(kāi)始支持的。假設(shè)有一個(gè)方法需要Derived集合作為參數(shù),那么基于上面的原則,我們會(huì)這樣設(shè)計(jì):
static void TestIn(IEnumerable<Base> bases) { }
接著我們向下面這樣調(diào)用,在3.5下就會(huì)發(fā)現(xiàn)編譯不通過(guò),提示無(wú)法將 List<Derived>轉(zhuǎn)換為IEnumerable<Base>。
List<Derived> listIn = new List<Derived>(); TestIn(listIn);
同樣的代碼,我們拿到4.0下,發(fā)現(xiàn)編譯通過(guò)了。比較 IEnumerable泛型接口,我們發(fā)現(xiàn)4.0下的定義為:
public interface IEnumerable<out T> : IEnumerable
發(fā)現(xiàn)多了 out 關(guān)鍵字,這就是協(xié)變。msdn對(duì)于類(lèi)型參數(shù)的解釋是:out T 要枚舉的對(duì)象的類(lèi)型。該類(lèi)型參數(shù)是協(xié)變的。即可以使用指定的類(lèi)型或派生程度更高的類(lèi)型。
我們可以這樣理解協(xié)變,參數(shù)的類(lèi)型就是協(xié)變的,父類(lèi)用子類(lèi)代替,也就是子類(lèi)當(dāng)父類(lèi)使用。
理解協(xié)變后,抗變就好理解了。函數(shù)的返回值就是抗變的,子類(lèi)用父類(lèi)代替,也就是父類(lèi)當(dāng)子類(lèi)使用。在非泛型的情況下,我們可以這樣接收方法的返回值:
object obj = Test_3(); static string Test_3() { return "hello world"; }
當(dāng)然,我們覺(jué)得這樣調(diào)用也應(yīng)該是可以的:
IEnumerable<Base> listOut = TestOut(); static IEnumerable<Derived> TestOut() { return new List<Derived>(); }
在3.5下,這樣同樣會(huì)編譯錯(cuò)誤。4.0下就沒(méi)有問(wèn)題。
三、總結(jié)
協(xié)變與抗變的概念其實(shí)我們經(jīng)常遇到(參數(shù)協(xié)變、返回值抗變),而且我們也會(huì)習(xí)慣的這樣設(shè)計(jì)。但對(duì)于泛型,.net 到了4.0才提供這樣的支持,這為泛型的使用提供了更大的靈活性。
ok,實(shí)際我們不怎么需要去理解概念性的東西,知道原理和理解怎么使用即可。以上是我的個(gè)人理解,如果有朋友想要更深入的理解,可以參見(jiàn)msdn。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注