//协变 IMotion<Teacher> x = new Run<Teacher>(); IMotion<People> y = (IMotion<People>)x; //逆变 IMotion<People> x2 = new Run<People>(); IMotion<Teacher> y2 = (IMotion<Teacher>)x2; IMotion<Teacher> y3 = (IMotion<Teacher>)new Run<People>();
天才的我发现编译成功了,没有任何问题!且还可以同时协变、逆变??不对,真的天才了吗?我们运行试试:
看来我还是太单纯了,如果真的这么容易绕过去,Microsoft又何必去搞个out、in关键字。
对于同一个泛型参数,我们既想有协变性又想逆变性,咋办?答案是不可行。这就会出现第三种情况,既不可以协变又不可以逆变。称为不变性。
如(我们在IMotion定义两个方法):
IMotion<T> { T Show(); void Match(T t); }
上面我们测试过,代码直接强制转换是不能实现协变、逆变的。那么我们只能通过out、in来实现。如果现在我们在泛型参数添加out或in属性会如何?:
我们发现out和in都不能用。在用out时,有个传入参数为泛型 void Match(T t) 的方法。使用in时,有个返回参数为泛型 T Show() 的方法。现在就出现了是矛更锋利,还是盾更坚硬的问题了。
最后结果是:都不能用,既不能协变,也不能逆变。此为不变体。
小知识:
C#4.0之前 IEnumerable<T> 、 IComparable<T> 、 IQueryable<T> 等接口都不支持可变性,在4.0及之后才支持。因为4.0之前定义的泛型接口没有添加out、in关键字,有兴趣可以切换版本看看。
延伸思考为什么in[输入参数]就只能逆变?分析如下:
People { } Teacher : People { { get; set; } } IMotion<in T> { void Match(T t); } Run<T> : IMotion<T> { public void Match(T t) { //假设中间有很多逻辑..... } }
为什么out[返回值]只能协变?分析如下:
People { } Teacher : People { Salary { get; set; } } IMotion<out T> { T Show(); //void Match(T t); } Run<T> : IMotion<T> { public T Show() { return default(T); } //public void Match(T t) //{ // //假设中间有很多逻辑..... //} }
这里有两个关键点:
。。。是不是有点越想越头晕,想不明白就慢慢想。自己动动手。
如果实在想的头大,就把它当成是乌龟的屁股(龟腚\规定)吧,知道是C#做的一种安全限制!
总结关于泛型接口、泛型委托的可变性:
IEnumerable<string> list = new List<string>();
IEnumerable<object> list2 = list; //协变
IEnumerable<object> list2 = new List<string>(); //(也可以直接写成这样)
IComparable<
注意:
好了,今天就到这里。没啥高深的技术知识,主要为理解协变、逆变、不变体等术语和概念。
本文已同步至索引目录:《》
同类文章推荐: