詳解c# 協(xié)變和逆變
基本概念
協(xié)變:能夠使用比原始指定的派生類型的派生程度更大(更具體)的類型。例如 IFoo<父類> = IFoo<子類>
逆變:能夠使用比原始指定的派生類型的派生程度更新(更抽象)的類型。例如 IBar<子類> = IBar<父類>
關(guān)鍵字out和in
協(xié)變和逆變在泛型參數(shù)中的表現(xiàn)方式,out關(guān)鍵字表示協(xié)變,in關(guān)鍵字表示逆變。二者只能在泛型接口或者委托中使用。
理解協(xié)變和逆變
看完上面的定義是不是一臉懵逼~~~??床欢蛯α?,且定義語句的歧義性很大。讓我們大腦趕緊清空下?。∈紫扔涀∫稽c(diǎn)明確的概念,類的多態(tài)展示一定是通過基類來表示,派生的具體類都是可轉(zhuǎn)化為基類,而不能走相反的流程。
下面我們用代碼直觀的表現(xiàn)下協(xié)變和逆變。
public class Animal { public void Eat() { } } public class Dog : Animal { public void Run() { } }
這是一段很簡單的子類和父類的關(guān)系,我們進(jìn)行一下簡單的轉(zhuǎn)化,應(yīng)該很好理解,Dog子類可以用Animal父類展示,反過來則不可以,會編譯錯(cuò)誤。
Dog dog = new Dog(); Animal animal = dog; //error 編譯錯(cuò)誤 //Dog dog2 = animal;
那么我們做一點(diǎn)變化。
List<Dog> dogs = new List<Dog>(); //error 編譯錯(cuò)誤 //List<Animal> animals_2 = dogs; IEnumerable<Dog> dogs_2 = dogs; IEnumerable<Animal> animals = dogs_2;
感覺到一點(diǎn)問題沒?Dog子類可以用Animal父類展示,使用List泛型就不可以了,但是IEnumerable泛型又可以。List<>和IEnumerable<>有什么不同?我們看下二者的定義即可發(fā)現(xiàn)端倪。
//IList定義 public interface IList<[NullableAttribute(2)] T> : ICollection<T>, IEnumerable<T>, IEnumerable {} //和IEnumerable定義 public interface IEnumerable<[NullableAttribute(2)] out T> : IEnumerable {}
區(qū)別就在于 IEnumerable的泛型參數(shù)用了out協(xié)變標(biāo)注,所以可以做正確的轉(zhuǎn)換。 這里也可以理解出什么時(shí)候需要使用in、out關(guān)鍵字:當(dāng)你設(shè)計(jì)帶有泛型的基類且泛型類型可能存在擴(kuò)展時(shí),則需要考慮使用in或者out關(guān)鍵字修飾。
我們再看看官方的Action<>和Func<>類對協(xié)變和逆變的使用,先看定義:
public delegate void Action<[NullableAttribute(2)] in T>(T obj); public delegate TResult Func<[NullableAttribute(2)] in T, [NullableAttribute(2)] out TResult>(T arg);
Action的泛型類型是入?yún)ⅲ胕n表示逆變,F(xiàn)unc的第二個(gè)泛型類型TResult是出參,用out表示協(xié)變。
那么這樣看起來對in、out關(guān)鍵字的認(rèn)識就很簡單明了了??纯崔D(zhuǎn)換示例:
Action<Dog> action_dog = d => d.Run(); Action<Animal> action_animal = a => a.Eat(); //error 編譯錯(cuò)誤。in //Action<Animal> action_animal_2 = action_dog; //Action泛型多態(tài)化 Action<Dog> action_dog_2 = action_animal; Func<int, Dog> func_dog = a => { return new Dog(); }; Func<int, Animal> func_animal = a => { return new Animal(); }; //Func泛型多態(tài)化 Func<int, Animal> func_animal_2 = func_dog; //error 編譯錯(cuò)誤。out //Func<int, Dog> func_dog_2 = func_animal;
注意注釋編譯錯(cuò)誤的語句,符合上面我們轉(zhuǎn)換的規(guī)則。對于入?yún)?,擴(kuò)展類可以替代基類參數(shù)輸入,用in修飾;對于出參,擴(kuò)展類可以替代基類返回輸出,用out修飾。相反則都不可以。
最后簡單總結(jié)下:
- 什么是協(xié)變/逆變?不要去想官方定義?。。。≈灰涀ut是協(xié)變,in是逆變即可。
- 為什么需要使用協(xié)變-out、逆變-in。在泛型或委托中,如果不使用協(xié)變/逆變,那么泛型類型一個(gè)精確的、固定的某一類型。而使用協(xié)變/逆變的話,則泛型類型可以實(shí)現(xiàn)多態(tài)化。但必須區(qū)分入?yún)⑹褂胕n,出參使用out。
以上就是詳解c# 協(xié)變和逆變的詳細(xì)內(nèi)容,更多關(guān)于c# 協(xié)變和逆變的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#中累加器函數(shù)Aggregate用法實(shí)例
這篇文章主要介紹了C#中累加器函數(shù)Aggregate用法,實(shí)例分析了C#中累加器的實(shí)現(xiàn)與使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07C# List<T> Contains<T>()的用法小結(jié)
本篇文章主要是對C#中List<T> Contains<T>()的用法進(jìn)行了總結(jié)介紹,需要的朋友可以過來參考下,希望對大家有所幫助2014-01-01Winform讓DataGridView左側(cè)顯示圖片
本文主要介紹在如何讓DataGridView左側(cè)顯示圖片,這里主要講解重寫DataGridView的OnRowPostPaint方法,需要的朋友可以參考下。2016-05-05C#使用iTextSharp將PDF轉(zhuǎn)成文本的方法
這篇文章主要介紹了C#使用iTextSharp將PDF轉(zhuǎn)成文本的方法,涉及C#操作pdf文件的相關(guān)技巧,需要的朋友可以參考下2015-05-05