C#进阶-协变与逆变

2024-02-03 10:35:46 浏览数 (2)

我们知道子类转换到父类,在C#中是能够隐式转换的。这种子类到父类的转换就是协变。而另外一种类似于父类转向子类的变换,可以简单的理解为逆变。逆变协变可以用于泛型委托和泛型接口,本篇文章我们将讲解C#里逆变和协变的使用。逆变和协变的语法第一次接触难免感到陌生,最好的学习方式就是在项目中多去使用,相信会有很多感悟。

协变与逆变

协变(共变):泛型委托或泛型接口的类似于父类转向子类的变换; 逆变(反变):泛型委托或泛型接口的类似子类到父类的隐式转换;

逆变与协变用来描述类型转换后的继承关系,其定义:如果A、B表示类型,f(x)表示类型转换,≤表示继承关系(比如,A≤B表示A是由B派生出来的子类) 当A≤B时,若f(x)是逆变的,则f(B)≤f(A)成立; 当A≤B时,若f(x)是协变的,则f(A)≤f(B)成立;

举个例子: string是object的子类,但List<string>和List<Object>没有继承关系,如果想实现List<string>和List<Object>的继承关系,我们就要使用逆变和协变。 令List<string>成为List<Object>子类的变化,我们叫做协变,和string与Object的父子关系是相同的;反之,令List<Object>成为List<string>子类的变化,我们叫做逆变,和string与Object的父子关系是相反的;

协变和逆变能够实现数组类型委托类型泛型类型参数的隐式引用转换。

演示代码:

代码语言:javascript复制
class Program
{
  /*泛型委托*/
  public delegate T DelegateFuncA<out T>();//支持协变
  public delegate void DelegateFuncB<in T>(T param);//支持逆变

  /*泛型接口*/
  public delegate T InterfaceFuncA<out T>();//支持协变
  public delegate void InterfaceFuncB<in T>(T param);//支持逆变

  static void Main(string[] args)
  {
  
    //泛型委托-协变
    /*object父类变成string子类*/
    DelegateFuncA<object> funcObject = null;
    DelegateFuncA<string> funcString = null;
    funcObject = funcString;//协变

    //泛型委托-逆变
    /*string子类变成object父类*/
    DelegateFuncB<object> actionObject = null;
    DelegateFuncB<string> actionString = null;
    actionString = actionObject;//逆变

    //泛型接口-协变
    InterfaceFuncA<object> InterfaceFuncObject = null;
    InterfaceFuncA<string> InterfaceFuncString = null;
    InterfaceFuncA<int> InterfaceFuncInt = null;
    InterfaceFuncObject = InterfaceFuncString;//变了,协变
    InterfaceFuncObject = InterfaceFuncInt;//编译失败,值类型不参与协变或逆变

    //泛型接口-逆变
    InterfaceFuncB<object> InterfaceFuncObjectB = null;
    InterfaceFuncB<string> InterfaceFuncStringB = null;
    InterfaceFuncStringB = InterfaceFuncObjectB;//变了,逆变

    //数组-逆变
    //数组的协变使派生程度更大的类型的数组能够隐式转换为派生程度更小的类型的数组。
    object[] array = new string[10];
    array[0] = 10;

  }
}

0 人点赞