先使用框架定义的泛型委托Func和Action做例子(不了解的)
协变:(string->object)
Func<; Func<object> func2 = func1;
逆变:(object->string)
Action<object> func3 = t => { }; Action<string> func4 = func3;
上面代码没有任何问题。
接着我们自己定义委托试试:
我X,看人不来哦。为什么自定义的委托却不能协变呢。
我看看系统定义的Func到底和我们自定义的有什么不同:
public delegate TResult Func<out TResult>();
多了一个out,什么鬼:
那么我们可以修改自定义委托:
完美!
那如果我们要实现逆变性呢:
直接逆变是不可行的,我们需要修改泛型类型参数:
我们发现整个委托参数都变了。本来的返回值,改成输入参数才行。
结论:
假设:如果泛型参数中既存在in又存在out改如何:
delegate Tout MyFunc<in Tin, out Tout>(Tin obj);
MyFunc<; MyFunc<string, string> str2 = str1;//第一个泛型的逆变(object->string) MyFunc<object, object> str3 = str1;//第二个泛型的协变(string->object) MyFunc<string, object> str4 = str1;//第一个泛型的逆变和第二个泛型的协变
以上都是没有问题的。
然后我们看看编译后的C#代码:
结论:
以上代码也可以直接写成:
//delegate Tout MyFunc<in Tin, out Tout>(Tin obj); MyFunc<; MyFunc<; MyFunc<;
泛型接口的可变性接着看框架默认接口:
协变:(子类->父类)
IEnumerable<string> list = new List<string>(); IEnumerable<object> list2 = list;
逆变:(父类-> 子类)
IComparable<object> list3 = null; IComparable<string> list4 = list3;
接下来我们试试自定泛型接口:
首先定义测试类型、接口:
People { } Teacher : People { } IMotion<T> { } Run<T> : IMotion<T> { }
然后我们测试协变性:
同样我们需要把接口 interface IMotion<T> 定义为 interface IMotion<out T>
IMotion<out T>{}
IMotion<Teacher> x = new Run<Teacher>(); IMotion<People> y = x;
如果我们要测试逆变性,则需要把 interface IMotion<T> 定义为 interface IMotion<in T>
IMotion<in T>{}
IMotion<People> x2 = new Run<People>(); IMotion<Teacher> y2 = x2;
泛型接口的逆变,编译后同样进行了强制转换:
当然,我们也可以直接写成:
IMotion<Teacher> y3 = new Run<People>();
不变性从上面我们知道逆变性的代码编译后都会进行强制转换。假设:那我们不用out、in直接手动强制转换是否可以?:
People { } Teacher : People { } IMotion<T> { } Run<T> : IMotion<T> { }