打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
一个简单例子理解C#的协变和逆变

关于协变逆变,SolidMango的解释是比较可取的。有了协变,比如,在需要返回IEnumerable<object>类型的时候,可以使用IEnmerable<string>来替代;有了逆变,比如,在需要接收IComparable<string>类型形参方法中,可以使用IComparable<object>类型实参来替代。

协变

先来体会协变。有2个具有继承关系的父类和子类。

    public class Animal    {        public string Name { get; set; }    }    public class Dog : Animal    {        public Dog(string dogName)        {            Name = dogName;        }    }

现在有一个帮助类的方法的形参类型是父类集合IEnumerable<Animal>。

    public class MyHelper    {        public void PrintAnimalNames(IEnumerable<Animal> animals)        {            foreach (var animal in animals)            {                Console.WriteLine(animal.Name);            }        }    }

有了协变,可以在PrintAnimalNames方法中传入IEnumerable<Dog>类型的实参替代IEnumerable<Animal>类型。

        static void Main(string[] args)        {            List<Dog> dogs = new List<Dog>()            {                new Dog("小狗petty"),                new Dog("小狗lily")            };            //协变            IEnumerable<Animal> animals = dogs;            MyHelper myHelper = new MyHelper();            myHelper.PrintAnimalNames(animals);            Console.ReadKey();        }

可见,在方法中基于基类接口类型的形参,调用该方法的时候可以传入派生类接口类型的实参。      

 

逆变

再来体会逆变。依然是2个具有继承关系的父类和子类。

    public class Animal     {        public string Name { get; set; }        public int Age { get; set; }    }    public class Cat : Animal    {        public Cat(string catName, int catAge)        {            Name = catName;            Age = catAge;        }    }

现在,我们想比较基类Animal的两个实例,为此,有必要专门写一个类让他实现IComparer<Animal>接口。

    public class AnimalSizeComparator : IComparer<Animal>    {        public int Compare(Animal x, Animal y)        {            if (x != null && y != null)            {                if (x.Age > y.Age)                {                    return 1;                }                else if (x.Age == y.Age)                {                    return 0;                }                else                {                    return -1;                }            }            else            {                return -1;            }        }    }

在帮助类中的方法中,针对Cat进行比较,方法接收IComparer<Cat>类型的形参。

    public class MyHelper    {        public void CompareCats(IComparer<Cat> catComparer)        {            var cat1 = new Cat("小猫1",1);            var cat2 = new Cat("小猫2",2);            if (catComparer.Compare(cat2, cat1) > 0)            {                Console.WriteLine("小猫2胜出");            }            else            {                Console.WriteLine("小猫1胜出");            }        }    }    

有了逆变,客户端调用MyHelper的CompareCats方法时,可以传入IComparer<Animal>类型的实参。

            IComparer<Animal> animalComparer = new AnimalSizeComparator();            MyHelper myHelper = new MyHelper();            myHelper.CompareCats(animalComparer);            Console.ReadKey(); 

可见,在方法中基于派生类接口类型的形参,调用该方法的时候可以传入基类接口类型的实参。 

总结:在本篇的场景中,派生类接口替代父类接口,称之为协变;父类接口代替派生类接口,称之为逆变。

 
 
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
父类引用指向子类对象:父类名 对象名=new 子类名();
C#中的多态性
C#关于多态的一个有趣的例子
《Java面向对象程序设计》09 抽象类与接口写字字帖
Java中的「接口」到底是什么?
【算法练习】校招研发工程师笔试题
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服