C# 中泛型接口的协变和逆变(抗变)

在C#中声明泛型接口时,我们可以使用 in 和 out 参数来控制这个泛型是协变还是逆变的,这里逆变有时也被翻译成抗变,协变和逆变是用来描述如果泛型存在继承关系时,两个泛型类是否能够直接赋值的问题。比如派生泛型 IInterface 是否能被赋值给 IInterface

被in修饰的泛型在接口中只能用于输入参数,是逆变的。例如我有父类和子类:

public class Parent { } public class Child : Parent { }

然后我定义了一个in修饰的泛型接口:

public interface IContravariant { 
    void getInput(T input); 
    //T getout(); // 这种会报错, in 类型的T只可以用于输入 
}

如果T标识成in,那么它只能用于输入,否则会报错。

然后我定义一个实现此接口的类:

public class class1 : IContravariant {
   public void getInput(T input) { } 
}

那么接下来,得益于in的担保,我们可以确保一个 class1 会把Parent类型用在参数输入而不是其它方面(似乎想起这样的话:给我买个手机吧,我保证用于学习而不是其他方面...)。那么对于一个class1的实例类,当我们使用接口的Child泛型方法去调用时,总会传给他Child类型的参数,而它本身的参数是接收Parent类型的,肯定没毛病。

IContravariant v1 = new class1();
//ITestInterface v2 = new myClass(); // 会报错,不允许类型转换

对于out修饰的协变泛型,情况类似:

public interface ICovariant {
   T getOut(); 
    //void testInput(T input); //这种会报错,out类型的T只允许用于返回值
} 

public class class2 : ICovariant where T : new() 
{ 
    //这里用where约束是因为我需要用 new 来返回一个T类型
    public T getOut() { return new T(); } 
}

得益于out的担保,T一定会应用于返回值而不是别的。那么对于一个class2的类,我们使用接口的Parent泛型方法去调用时,接收Parent类型的返回值,而这个实例类实际返回Child类型的方法,也保证了没有毛病。

//ICovariant v3 = new class2(); // 会报错,不允许类型转换
ICovariant v4 = new class2();

如果实在还不理解就这么记忆吧:

  • 协变,很外向(out修饰)很和谐,子类无伤转换为父类,非常和谐。
  • 逆变,很内向(in)很拧巴,父类别扭地转换为子类。

好吧,祝大家都外向一些,你看程序代码都这么设计了。天意,简直是天意!!

同时,注意 in 和 out 参数只可以用来定义泛型接口或者委托类型参数。

转载请注明出处。如果您觉得本文有用,欢迎点赞。
更多教程请在网易云课堂,B站, 优酷或腾讯视频搜索黑山老雕。

你可能感兴趣的:(C# 中泛型接口的协变和逆变(抗变))