协变与逆变的简要说明

11 篇文章 0 订阅

在看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<out T>();
        static void Main(string[] args)
        {
            MyFunc_cov<Base> myFunc_Cov_Base = ( b ) => { };
            MyFunc_cov<Derived> myFunc_Cov_Derived;

            myFunc_Cov_Derived = myFunc_Cov_Base;

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

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

   delegate T MyFunc_invert<out T>();

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

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

in关键字指定该类型参数是逆变的,反转了继承关系,A<B结果f(B)<f(A),上述代码中测试逆变的委托:

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

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

因为Derived<Base所以MyFunc_cov<Base> < MyFunc_cov<Derived>

参考:

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

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

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值