协变与逆变的简要说明

在看C#的时候看到了协变与逆变,百度一下发现原来C++, java里早就有了协变与逆变.

首先说明协变与逆变的含义:

逆变与协变用来描述类型转换(type transformation)后的继承关系,其定义:如果A、B表示类型,f(⋅)表示类型转换,≤表示继承关系(比如,A≤B表示A是由B派生出来的子类)
f(⋅)是逆变(contravariant)的,当A≤B时有f(B)≤f(A)成立;
f(⋅)是协变(covariant)的,当A≤B时有f(A)≤f(B)成立;
f(⋅)是不变(invariant)的,当A≤B时上述两个式子均不成立,即f(A)与f(B)相互之间没有继承关系。

上述内容网上很多博客都有介绍,并且从上面的描述来看,对于某一个参数来说逆变,协变,不变只能选其一,不能既是协变又是逆变,因为

fA)<=f(B)f时不会有(B)<=f(A)。

为了详细说明协变与逆变,介绍下里氏替换原则:

所有引用基类的地方必须能透明地使用其子类的对象。也就是说基类引用(或指针)可以指向子类对象。

C++ Primer中有:从派生类向基类的类型转换只对指针或引用类型有效,说的就是里氏替换原则。

为了满足里氏替换原则于是就有了协变与逆变。在博客 https://www.cnblogs.com/pyes/p/4907776.html 看到的这句话(当然有可能是书上的)觉得很好的解释了协变逆变的作用:

参数逆变:正是因为需要符合里氏替换法则,方法中的参数类型声明时必须符合逆变(或不变),以让子类方法可以接收更大的范围的参数(处理能力增强);而不能声明为协变,子类方法可接收的范围是父类中参数类型的子集(处理能力减弱)。
返回值协变:如果结果类型是逆变的,那子类方法的处理能力是减弱的,不符合里氏替换。因此返回值类型声明时必须符合协变(或不变)

现在看下c++和C#中的协变与逆变。

对于C++里的指针和引用显然是符合协变的,而为了让模板协变,在C++里需要提供特殊的复制构造函数和特殊的重载赋值运算符,逆变则是通过std::function实现,具体参考博客:https://www.jianshu.com/p/db76a8b08694,比较复杂。

C#里则有关键字out, in,手动实现比较简单,比如:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TestCovInvert
{
    class Base{ }

    class Derived : Base {  }

    class Program
    {
        delegate void MyFunc_cov < in T >( T obj );//逆变
        delegate T MyFunc_invert();
        static void Main(string[] args)
        {
            MyFunc_cov myFunc_Cov_Base = ( b ) => { };
            MyFunc_cov myFunc_Cov_Derived;

            myFunc_Cov_Derived = myFunc_Cov_Base;

            MyFunc_invert myFunc_Invert_Base;
            MyFunc_invert myFunc_Invert_Derived = () => { return new Derived(); };
            myFunc_Invert_Base = myFunc_Invert_Derived;
        }
    }
}

out 关键字指定该类型参数是协变的,让是返回值保留继承关系,所以上面代码中测试协变的委托是这样的:

   delegate T MyFunc_invert();

类型参数用out修饰,并且这里函数返回值类型为T,正是因为out关键字是为了让返回值保留继承关系所以叫out啊。

因为Derived继承于Base,使用了out保留了继承关系所以有 myFunc_Invert_Base = myFunc_Invert_Derived;

in关键字指定该类型参数是逆变的,反转了继承关系,A

 delegate void MyFunc_cov < in T >( T obj );//逆变

类型参数使用in修饰, 返回类型绝对不能是T,否则报错, 差异无效: 类型参数“T”必须是在“Program.MyFunc_cov.Invoke(T)”上有效的 协变式。“T”为 逆变。 

因为Derived < MyFunc_cov

参考:

https://www.jianshu.com/p/db76a8b08694

https://www.cnblogs.com/zhaopei/p/variability.html

你可能感兴趣的:(C#,C++)