.NET Core CSharp初级篇 1-8泛型、逆变与协变 (2)

例如:

// Cat:Animal //这种变化毫无问题 Cat c = new Cat(); Animal a = c; //报错,因为List<Cat>不继承于List<Animal> List<Cat> d = new List<Cat>(); List<Animal> = d;

对于泛型的参数,我们可以使用到我们之前讲函数参数的时候所遇到的 in,out 关键字。In代表输入,体现的就是逆变,Out代表输出,代表的是协变。对于Out输出的东西,自然不可以对他进行输入操作,他只能作为结果返回,因此它不会被修改。因此进行隐式转换的时候,编译器认为该转换是安全的(返回值不变)。

例如

IEnumerable<Cat> c = new List<Cat>(); IEnumerable<Animal> a = c;

很多人可能不不能很好地理解这些来自于物理和数学的名词。我们无需去了解他们的数学定义,但是至少应该能分清协变和逆变。实际上这个词来源于类型和类型之间的绑定。我们从数组开始理解。数组其实就是一种和具体类型之间发生绑定的类型。数组类型Int32[]就对应于Int32这个原本的类型。任何类型T都有其对应的数组类型T[]。那么我们的问题就来了,如果两个类型T和U之间存在一种安全的隐式转换,那么对应的数组类型T[]和U[]之间是否也存在这种转换呢?这就牵扯到了将原本类型上存在的类型转换映射到他们的数组类型上的能力,这种能力就称为“可变性(Variance)”。在.NET世界中,唯一允许可变性的类型转换就是由继承关系带来的“子类引用->父类引用”转换。举个例子,就是String类型继承自Object类型,所以任何String的引用都可以安全地转换为Object引用。我们发现String[]数组类型的引用也继承了这种转换能力,它可以转换成Object[]数组类型的引用,数组这种与原始类型转换方向相同的可变性就称作协变

逆变

逆变则恰恰与协变完全相反,逆变是指代类型往更小的派生类中进行转换,显然这有可能是不安全的,因为有可能会导致数据的丢失。在C#中使用逆变式的方法是使用In关键字,这意味着这个参数只能作为返回值返回,那么我们就有可能对传入的参数进行修改,因此使用强制转换有可能是不合法的。
例如:

public interface IMyList<in T> { T GetElement(); void ChangeT(T t); } public class MyList<T> : IMyList<T> { public T GetElement() { return default(T); } public void ChangeT(T t) { //Change T } }

这段代码无法通过编译,因为GetElement是将T返回,这显然不符合逆变的定义。逆变的参数只允许输入而不允许输出。

对于逆变的实践,各位可以去参阅下IList接口与IEnumerable接口的实现。这两个接口很好的体现了在集合中的逆变与协变。

(缺少举例说明,后续补上)

总结

对于泛型,并没有太多的奇技淫巧可言,因为泛型的出现已经就是一个奇技淫巧了。泛型最常用的地方是泛型数组。并且C#对于不确定类型和大小的数组会使用一个非常好用的类,叫做List类,我们将会在中级篇中进行详细的介绍。

如果我的文章帮助了您,请您在github .NET Core Guide项目帮我点一个star,在博客园中点一个关注和推荐。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpppfw.html