C# 协变 逆变

逆变,协变

定义:

逆变 :指能够使用比原始指定的派生类型的 派生程度更小(不太具体的)的类型。

协变 :能够使用比原始指定的派生类型的 派生程度更大(更具体的)的类型。

看了上面一脸懵逼,下面会解释

示例

这是用来讲解例子的两个类

public class Father
{
    public string Name { get; set; }
}

public class Son : Father
{
    public int Age { get; set; }
}

协变 out

Father f = new Son();

先来看这一句 这是我们熟悉的, 因为Son继承自Father, 所以编译可以通过。

我们可以认为:

Father(基类) 相对于 Son(子类) 是派生程度更大(更具体的)的类型。 具体,代表一个父类可以有多个子类。所以相对而言基类更具体 子类不太具体

Son(子类) 相对于 Father(基类) 是派生程度更小(不太具体的)的类型

再来看这一句

List<Son> sons = new List<Son>();
List<Father> fathers = sons; // error

为什么为编译失败呢,因为编译器知道 Father 和 Son 是继承关系,但是不知道List< Father > 和 List< Son > 是什么关系。

List<Son> sons = new List<Son>();
IEnumerable<Father> fathers = sons; // success

这是协变,可以允许使用比原指定类型派生程度更大(更具体的)的类型。

逆变 in
逆变也称为不正常的变化
比如Father转为Son,这肯定不行。


static void Main(string[] args)
{
    ISay<Father> fatherSay = new FatherSay();

    ISay<Son> son1 = fatherSay;
    son1.Say(new Son()); // new Son()这里就是 传的是派生程度更低的类型,  而指定的是父类,我对父类的操作肯定也可以传递到子类
} 

public interface ISay<in T>
{
    void Say(T t); 
}

public class FatherSay : ISay<Father>
{
    public void Say(Father t)
    {
        Console.WriteLine(t.Name);
    }
}

定义ISay接口 我指定的类型是Father 既然father有say().Name 为什么Son不能呢,所以使用逆变.

这是逆变 可以使用比原指定类型派生程度更小(不太具体的)的类型。

其实就是希望当作为返回的时候,期望的是派生程度更高的类型,你指定一个子类,我希望他能返回,他的父类,或者父类的父类。这样我才能保证输出的正确,比如我返回个父类,你却非要子类,而子类里入上例有一个属于自己的age属性,所以就不满足

而作为参数类型,我希望传的是派生程度更低的类型,因为你指定的是父类,我对父类的操作肯定也可以传递到子类。而为什么不能传派生程度更高的类型,比如你指定的是子类,但是传的是父类, 那你怎么保证对子类的操作,会传递到父类。

相当于
协变: Father f() => new Son(); 所以作为返回值时可以更具体
逆变:指定父类Father Say(Father f)=> f.Name我传入子类Say(Son s) => s.Name一定满足, 而如果我指定子类Son Say(Son s) => s.Age
然后传入父类 Say(Father f) => f.Age ?可以看到父类是没有age的 所以逆变我们需要传入派生程度更低(不具体)的类型

posted @ 2020-12-17 19:44  菜洋  阅读(144)  评论(0编辑  收藏  举报