在C#中声明泛型接口时,我们可以使用 in 和 out 参数来控制这个泛型是协变还是逆变的,这里逆变有时也被翻译成抗变,协变和逆变是用来描述如果泛型存在继承关系时,两个泛型类是否能够直接赋值的问题。比如派生泛型 IInterface<Child> 是否能被赋值给 IInterface<Parent>。
被in修饰的泛型在接口中只能用于输入参数,是逆变的。例如我有父类和子类:
public class Parent { } public class Child : Parent { }
然后我定义了一个in修饰的泛型接口:
public interface IContravariant<in T> {
void getInput(T input);
//T getout(); // 这种会报错, in 类型的T只可以用于输入
}
如果T标识成in,那么它只能用于输入,否则会报错。
然后我定义一个实现此接口的类:
public class class1<T> : IContravariant<T> {
public void getInput(T input) { }
}
那么接下来,得益于in的担保,我们可以确保一个 class1<Parent> 会把Parent类型用在参数输入而不是其它方面(似乎想起这样的话:给我买个手机吧,我保证用于学习而不是其他方面...)。那么对于一个class1<Parent>的实例类,当我们使用接口的Child泛型方法去调用时,总会传给他Child类型的参数,而它本身的参数是接收Parent类型的,肯定没毛病。
IContravariant<Child> v1 = new class1<Parent>();
//ITestInterface<Parent> v2 = new myClass<Child>(); // 会报错,不允许类型转换
对于out修饰的协变泛型,情况类似:
public interface ICovariant<out T> {
T getOut();
//void testInput(T input); //这种会报错,out类型的T只允许用于返回值
}
public class class2<T> : ICovariant<T> where T : new()
{
//这里用where约束是因为我需要用 new 来返回一个T类型
public T getOut() { return new T(); }
}
得益于out的担保,T一定会应用于返回值而不是别的。那么对于一个class2<Child>的类,我们使用接口的Parent泛型方法去调用时,接收Parent类型的返回值,而这个实例类实际返回Child类型的方法,也保证了没有毛病。
//ICovariant<Child> v3 = new class2<Parent>(); // 会报错,不允许类型转换
ICovariant<Parent> v4 = new class2<Child>();
如果实在还不理解就这么记忆吧:
- 协变,很外向(out修饰)很和谐,子类无伤转换为父类,非常和谐。
- 逆变,很内向(in)很拧巴,父类别扭地转换为子类。
好吧,祝大家都外向一些,你看程序代码都这么设计了。天意,简直是天意!!
同时,注意 in 和 out 参数只可以用来定义泛型接口或者委托类型参数。
转载请注明出处。如果您觉得本文有用,欢迎点赞。
更多教程请在网易云课堂,B站, 优酷或腾讯视频搜索黑山老雕。

网友评论