协变与逆变

前言

在C#编程中,由于存在类型之间的强制转换,很容易会出现所谓的类型可变性说法,存在协变、逆变、不变三种。

如果创建了泛型类型的实例,编译器会接受泛型类型声明以及类型参数来创建构造类型。但是在日常使用过程中,我们可能会将派生类型分配给基类型的变量,有时候会出现错误。

这里就存在一个赋值兼容性问题。

每一个变量都有一种类型,可以将派生类对象的实例赋值给基类变量(好比之前子类声明的变量可以赋值给父类声明的变量一样)。

如下所示:

//子类赋值给父类
People ahui = new People();
People people = new APeople();

Console.WriteLine("Age:" + people.Age);

//父类
class People
{
    public int Age = 27;
}


//子类
class APeople : People
{

}

协变

我们按照同样的逻辑,在泛型委托中进行这种强类型的转换,会发现即使基类和派生类之间可以进行正常的转换,但是委托之间不能进行转换会出现异常错误提示。

具体如下代码所示:

实现:

 //协变
   delegate T AgeDelegate< T>();

   public class Covariance
   {
       public static APeople GetAge()
        {
            return new APeople();
        }
      
   }

调用:

//外部调用
AgeDelegate<APeople> aPeople = Covariance.GetAge;
AgeDelegate<People> people = aPeople;

上面代码直接报红提示错误:
image

这就是上面解释的那样子,基类和派生类之间可以进行转换但是委托之间未存在关联,无法进行强制类型的转换。那么想解决这个问题就引入了协变来解决。

如果派生类只是用于输出值,那么这种结构化的委托有效性之间的常数关系叫做协变,可通过主动告知编译器我们的期望,使用Out关键字标记委托声明中的类型参数。

只需要在泛型委托T前加上out修饰,修改成如下这样子后,上面错误演示的代码编译器就可以正常编译通过了。

 delegate T AgeDelegate<out T>();  

逆变

其实逆变就是在委托中既要声明委托类型,也要在委托方法中有实参。

这种在期望传入基类时允许传入派生对象的特性叫做逆变。逆变使用关键字in来标记。

具体如下代码所示:

实现:

//逆变
delegate void AgeDelegate<in T>(T p);

public class Inversion
{
    public static void GetAge(People p)
    {
        Console.WriteLine(p.Age);
    }
}

调用:

AgeDelegate<People> aPeople = Inversion.GetAge;
AgeDelegate<APeople> people = aPeople;

people(new APeople());

posted @ 2022-07-30 09:14  码农阿亮  阅读(76)  评论(0编辑  收藏  举报