[C.C++] C#实现协变和逆变案例

2617 0
黑夜隐士 2022-11-5 08:50:51 | 显示全部楼层 |阅读模式
关于协变逆变,SolidMango的解释是比较可取的。有了协变,比如,在需要返回IEnumerable<object>类型的时候,可以使用IEnmerable<string>来替代;有了逆变,比如,在需要接收IComparable<string>类型形参方法中,可以使用IComparable<object>类型实参来替代。
协变

先来体会协变。有2个具有继承关系的父类和子类。
  1.     public class Animal
  2.     {
  3.         public string Name { get; set; }
  4.     }
  5.     public class Dog : Animal
  6.     {
  7.         public Dog(string dogName)
  8.         {
  9.             Name = dogName;
  10.         }
  11.     }
复制代码
现在有一个帮助类的方法的形参类型是父类集合IEnumerable<Animal>。
  1.     public class MyHelper
  2.     {
  3.         public void PrintAnimalNames(IEnumerable<Animal> animals)
  4.         {
  5.             foreach (var animal in animals)
  6.             {
  7.                 Console.WriteLine(animal.Name);
  8.             }
  9.         }
  10.     }
复制代码
有了协变,可以在PrintAnimalNames方法中传入IEnumerable<Dog>类型的实参替代IEnumerable<Animal>类型。
  1.         static void Main(string[] args)
  2.         {
  3.             List<Dog> dogs = new List<Dog>()
  4.             {
  5.                 new Dog("小狗petty"),
  6.                 new Dog("小狗lily")
  7.             };
  8.             //协变
  9.             IEnumerable<Animal> animals = dogs;
  10.             MyHelper myHelper = new MyHelper();
  11.             myHelper.PrintAnimalNames(animals);
  12.             Console.ReadKey();
  13.         }
复制代码
可见,在方法中基于基类接口类型的形参,调用该方法的时候可以传入派生类接口类型的实参。      
逆变

再来体会逆变。依然是2个具有继承关系的父类和子类。
  1.     public class Animal
  2.     {
  3.         public string Name { get; set; }
  4.         public int Age { get; set; }
  5.     }
  6.     public class Cat : Animal
  7.     {
  8.         public Cat(string catName, int catAge)
  9.         {
  10.             Name = catName;
  11.             Age = catAge;
  12.         }
  13.     }
复制代码
现在,我们想比较基类Animal的两个实例,为此,有必要专门写一个类让他实现IComparer<Animal>接口。
  1.     public class AnimalSizeComparator : IComparer<Animal>
  2.     {
  3.         public int Compare(Animal x, Animal y)
  4.         {
  5.             if (x != null && y != null)
  6.             {
  7.                 if (x.Age > y.Age)
  8.                 {
  9.                     return 1;
  10.                 }
  11.                 else if (x.Age == y.Age)
  12.                 {
  13.                     return 0;
  14.                 }
  15.                 else
  16.                 {
  17.                     return -1;
  18.                 }
  19.             }
  20.             else
  21.             {
  22.                 return -1;
  23.             }
  24.         }
  25.     }
复制代码
在帮助类中的方法中,针对Cat进行比较,方法接收IComparer<Cat>类型的形参。
  1.     public class MyHelper
  2.     {
  3.         public void CompareCats(IComparer<Cat> catComparer)
  4.         {
  5.             var cat1 = new Cat("小猫1",1);
  6.             var cat2 = new Cat("小猫2",2);
  7.             if (catComparer.Compare(cat2, cat1) > 0)
  8.             {
  9.                 Console.WriteLine("小猫2胜出");
  10.             }
  11.             else
  12.             {
  13.                 Console.WriteLine("小猫1胜出");
  14.             }
  15.         }
  16.     }
复制代码
有了逆变,客户端调用MyHelper的CompareCats方法时,可以传入IComparer<Animal>类型的实参。
  1.             IComparer<Animal> animalComparer = new AnimalSizeComparator();
  2.             MyHelper myHelper = new MyHelper();
  3.             myHelper.CompareCats(animalComparer);
  4.             Console.ReadKey();
复制代码
可见,在方法中基于派生类接口类型的形参,调用该方法的时候可以传入基类接口类型的实参。
总结:在本篇的场景中,派生类接口替代父类接口,称之为协变;父类接口代替派生类接口,称之为逆变。
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对中国红客联盟的支持。如果你想了解更多相关内容请查看下面相关链接
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行