C#协变和逆变

2021-03-11 11:28

阅读:371

标签:guid   each   for   mamicode   接口   guide   nim   ros   代码   

本篇博客所讲的是C#泛型中的协变和逆变。

首先讲协变:

协变

要把泛型参数定义为协变,可在类型定义中使用out关键字,例如:

 public interface IEnumerableout T> : IEnumerable
 {
        IEnumerator GetEnumerator();
 }

相信这个方法,大伙都知道,这个是C#中内置的一个IEnumerable接口。

假定A可以转化为B,如果X可以转为为B,那么称X有一个协变类型参数。

          {

                IEnumerablestring> str = new Liststring> { "Zero", "One", "Two" };
                IEnumerableobject> obj = str;

                foreach (var item in obj)
                {
                    Console.WriteLine(item);
                }
            }

比如这段代码,它能不能执行呢?能的,执行结果如图:

技术图片

 

 如果我把IEnumerable obj换成IEnumerable obj,上面这段代码肯定是没法跑的了,这就衍生出一个问题,如果两个类没有父子关系,它们是没有办法协变的,

根据上面的例子,我们知道C#中Object是最底层的基类,那么string类型是继承object类型的,通过协变IEnumerable是能够隐式转为IEnumerable

C#中有内置的IEnumerable,那我们如何自己在日常开发中用到协变呢。

}
public class Cat : Animal
{

}

 /// 
    /// 定义协变
    /// 
    /// 
    public interface IAnimalout T>:IAnimal
    {
    
    }
    /// 
    /// 抽象动物
    /// 
    public interface IAnimal
    {
        void Name(Animal animal);
    }
    /// 
    /// 具体的动物
    /// 
    /// 
    public class ConceteAnimal : IAnimal
    {
        public void Name(Animal animal)
        {
            Console.WriteLine($"这是{animal.Name}");
        }
    }
    //定义动物基类
    public class Animal
    {
        public string Name { get; set; }
    }
    public class Dog : Animal
    {

    }
    public class Cat : Animal
    {

    }

 

首先定义一个抽象的动物,然后定义一个协变接口,定义一个具体的动物实现抽象方法。

然后定义个一只猫,一只狗。

 ConceteAnimal cat = new ConceteAnimal();
 cat.Name(new Cat { Name = "" });
 IAnimal animal1 = cat;

当具体动物是一只猫的时候,执行方法。

技术图片

通过手写了一个例子,其实能够发现这个东西还是蛮好用的,但是不是很好理解,需要多去敲代码,才能体会到其中的奥妙。

协变的好处是能够很好的解决复用性的问题。协变仅仅对引用转换有效,对装箱是无效的。

协变在接口(interface)中是比较常见的,但是这个东西需要跟方法中的out参数是要做区分的,方法中的out参数是不支持协变的,这是CLR的限制。

逆变

其实这个也还能叫抗变,我看了《C#入门经典第七版》是叫抗变的,目前官方文档的叫法是逆变(https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/covariance-contravariance/),《C#图解教程》中泛型那一章也是叫逆变的,所有标题就叫逆变好了。

通过协变我们知道,假定A可以转化为B,如果X可以转为为B,那么称X有一个协变类型参数。协变正好相反,即,从X转换为X,它仅在类型参数出现在输入位置上,并且用in修饰符才能行。

下面上例子。

/// 
    /// 定义逆变
    /// 
    /// 
    public interface IAnimalin T> : IAnimal
    {

    }

把out修饰符换成in修饰符。

 ConceteAnimal animal = new ConceteAnimal();
animal.Name(new Animal { Name = "" });
IAnimal dog = animal;

执行代码。

技术图片

 

 通过上面的协变,再来看这个逆变,其实就是倒过来的,我在实际学习中是也是跟博客一样先学习的协变再学逆变,学逆变的时候感觉这个东西还是蛮通透的。

在c#中也是内置了一些逆变interface的,例如:IComparer,在这我就不去实现它了。

c#中的协变和逆变,都是基于泛型,所以例如泛型委托,也是会用到协变和逆变的,这个如果以后写委托的博客的时候再去写一下与之相关的。

写这种纯C#的知识博客,比写记录bug类多了,得看书,查资料。

 

C#协变和逆变

标签:guid   each   for   mamicode   接口   guide   nim   ros   代码   

原文地址:https://www.cnblogs.com/aqgy12138/p/12636735.html


评论


亲,登录后才可以留言!