日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

协变与逆变详细解读

發布時間:2025/7/14 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 协变与逆变详细解读 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

協變與逆變是.Net4.0新加入的概念,我看了很多博客文章,可能是我悟性比較差,感覺沒有完全講明白,自己研究了一天終于搞懂了,特此記錄一下。

一、簡單理解協變和逆變

//協變:子類對象(引用)賦值給父類變量(引用) object obj = null; string str = ""; obj = str; //逆變:父類對象(引用)賦值給子類變量(引用) object obj = null; string str = ""; str = obj; /* str = obj;這段代碼大家會發現是錯誤的,這個賦值是基本操作都是錯誤的, 那又如何實現逆變這種逆反的賦值操作呢?實際上逆變根本不是逆向將父類 賦給子類的,我慢慢解釋... */

二、真正的協變和逆變

概念:

1、以前的泛型系統(或者說沒有in/out關鍵字時),是不能“變”的,無論是“逆”還是“順(協)”。
2、當前僅支持接口和委托的逆變與協變 ,不支持類和方法。但數組也有協變性。
3、值類型不參與逆變與協變。

協變:Foo<ParentClass> = Foo<ChildClass>

public class TestOut<T> where T : new() {/** 關鍵字out因為協變的類型T只能作為輸出參數使用,而不能作為輸入參數*///*****[協變]泛型委托Demo*****//1、創建泛型委托public delegate T MyFunA<T>(); //默認不支持協變與逆變public delegate T MyFunB<out T>(); //設置支持協變//public delegate void MyFunC<out T>(T param);//錯誤,協變類型只能“出”不能“入”//2、創建委托變量public MyFunA<object> FunAObject = null;public MyFunA<string> FunAString = null;public MyFunB<object> FunBObject = null;public MyFunB<string> FunBString = null;public MyFunB<int> FunBInt = null;//3、驗證結果public void TestFun(){//FunAObject = FunAString;//錯誤,不可用協變FunBObject = FunBString;//正確,可用協變可以完成子類string到父類object的轉換//FunBObject = FunBInt; //錯誤,值類型不參與協變 }//*****[協變]泛型接口Demo*****//1、創建泛型接口public interface IMyInterfaceA<T> { } //默認不支持協變與逆變public interface IMyInterfaceB<out T> { } //設置支持協變//public interface IMyInterfaceC<out T>//{// void Test(T param);//錯誤,協變只能“出”不能“入”//}//2、創建接口變量public IMyInterfaceA<object> interAObject = null;public IMyInterfaceA<string> interAString = null;public IMyInterfaceB<object> interBObject = null;public IMyInterfaceB<string> interBString = null;public IMyInterfaceB<int> interBInt = null;//3、驗證結果public void TestInterface(){//interAObject = interAString; //錯誤,不可用協變interBObject = interBString; //正確,可用協變可以完成子類string到父類object的轉換//interBObject = interBInt; //錯誤,值類型不參與協變 } }

逆變:Foo<ChildClass> = Foo<ParentClass>

public class TestIn<T> where T : new() {/** 關鍵字in因為逆變的類型只能作為輸入參數,而不能作為輸出參數*///*****[逆變]泛型委托*****//1、創建泛型委托public delegate void MyActionA<T>(T param); //默認不支持協變與逆變public delegate void MyActionB<in T>(T param); //設置支持逆變//public delegate T MyActionC<in T>();//錯誤,逆變只能“入”不能“出”//2、創建委托變量public MyActionA<object> ActionAObject = null;public MyActionA<string> ActionAString = null;public MyActionB<object> ActionBObject = null;public MyActionB<string> ActionBString = null;public MyActionB<int> ActionBInt = null;//3、驗證結果public void TestAction(){//ActionAString = ActionAObject; //錯誤,不可用逆變ActionBString = ActionBObject; //正確,可用逆變可以完成從父類object到子類string的轉換//ActionBInt = ActionBObject; //錯誤,值類型不參與逆變 }//*****[逆變]泛型接口*****//1、創建泛型接口public interface IMyInterfaceA<T> { } //默認不支持協變與逆變public interface IMyInterfaceB<in T> { } //設置支持逆變//public interface IMyInterfaceC<in T>//{// T Test();//錯誤,逆變只能“入”不能“出”//}//2、創建接口變量public IMyInterfaceA<object> interAObject = null;public IMyInterfaceA<string> interAString = null;public IMyInterfaceB<object> interBObject = null;public IMyInterfaceB<string> interBString = null;public IMyInterfaceB<int> interBInt = null;//3、驗證結果public void TestInterface(){//interAString = interAObject; //錯誤,不可用逆變interBString = interBObject; //正確,可用你變可以完成從父類object到子類string的轉換//interBInt = interBObject; //錯誤,值類型不參與逆變 } }

三、解析協變與逆變(協變是順序的,逆變并不是逆反的)

這里就是我看別人的博客沒有看懂的地方,研究時從這里卡住了半天,想通后發現豁然開朗,現在分享出來

先創建一個協變接口,一個逆變接口

//*****協變接口***** public interface ITestA<out T> {T Test(); } public class TestA<T> : ITestA<T> {public T Test(){//do something...return default(T);} } //*****逆變接口***** public interface ITestB<in T> {void Test(T p); } public class TestB<T> : ITestB<T> {public void Test(T p){//do something... } }

協變解析:

internal class Program {private static void Main(string[] args){//寫法一ITestA<object> testA = new TestA<string>();object obj = testA.Test();//寫法二ITestA<object> testA1 = null;ITestA<string> testA2 = null;testA1 = testA2;obj = testA1.Test();/*執行步驟如下://先調用父類函數public object ITestA<object>.Test(){//發現父類函數為接口,函數體由子類實現,所以...//再調用子類函數public string ITestA<string>.Test(){//do something... }//父類函數調用子類函數,子類函數向外return返回值,由string類型傳至object類型}*///協變“可出不可入”因為由子類函數向父類函數返回值,子類類型小,父類類型大,所以可以進行安全轉換 } }

別的博客中看到以上解釋,沒看明白,后來才懂,他的意思是:協變時是子類向父類返回值,值類型是由子到父,可以安全轉換!
[原式就是主觀應該調用的方式,我想調用子類的這個函數]
[變式就是實際運行時調用的方式,先調用父類函數再由父類函數調用子類函數]

//ITest1<object> = ITest1<string> //原式(子類):string ITest1<string>.Test() ////變式(父類):object ITest1<object>.Test()

逆變解析:

internal class Program {private static void Main(string[] args){//寫法一ITestB<string> testB = new TestB<object>();testB.Test("");//寫法二ITestB<string> testB1 = null;ITestB<object> testB2 = null;testB1 = testB2;testB1.Test("");/*執行步驟如下://先調用父類函數public void ITestB<string>.Test(string param){//發現父類函數為接口,函數體由子類實現,所以...//再調用子類函數public void ITestB<object>.Test(object param){//do something... }//父類函數調用子類函數,并向子類函數傳遞參數由string類型傳至object類型}*///逆變“可入不可出”因為由父類函數向子類函數傳遞參數,父類類型小,子類類型大,所以可以進行安全轉換 } }

別的博客中看到以上解釋,沒看明白,后來才懂,他的意思是:逆變時是父類向子類傳參數值,值類型是由子到父,可以安全轉換!
[原式就是主觀應該調用的方式,我想調用子類的這個函數]
[變式就是實際運行時調用的方式,先調用父類函數再由父類函數調用子類函數]

//ITest2<string> = ITest2<object> //原式(子類):void ITest2<object>.Test(object param) ////變式(父類):void ITest2<string>.Test(string param)

?

調用執行步驟:
ITestA<object> testA = new TestA<string>();
object obj = testA.Test();
ITestB<string> testB = new TestB<object>();
testB.Test("");
父類變量(引用)調用方法,實際上執行步驟如下:
1、調用父類自己的方法
2、被告知方法體由子類實現
3、父類去調用子類方法
4、【逆變】發現子類方法有參,于是父類傳遞自己的參數(類型string)到子類(類型object),可以安全轉換
5、子類執行方法體功能
6、【協變】將執行的返回值返回給父類
7、【協變】父類接收子類方法返回值,返回值類型為子類的
8、【協變】繼續向上返回,發現返回值類型不一樣(類型string),所以轉為父類方法的類型返回(類型object),可以安全轉換

?

所以這就是為什么【協變只能返回值】,而【逆變只能傳遞值】,實際協變逆變并沒有父類型轉子類型的過程,都是使用的子類型轉父類型的安全轉換

應用場景:微軟提倡只要是泛型的接口或者委托都希望使用協變逆變,RedSharper也會有相應的提示,這樣做也可以增加【函數傳入參數值、【函數返回值】的擴展性,何樂而不為呢~

轉載于:https://www.cnblogs.com/taiyonghai/p/6524910.html

總結

以上是生活随笔為你收集整理的协变与逆变详细解读的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。