zoukankan      html  css  js  c++  java
  • 一个简单例子理解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(); 

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

          

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

  • 相关阅读:
    springboot redis多数据源
    springboot mybatis 多数据源配置
    java jackson 忽略不存在的属性字段 和 按照属性名转json
    java 四舍五入保留两位小数
    supervisord supervisorctl 问题supervisor.sock refused connection
    2
    1
    Python开发【程序】:三级菜单程序
    Python开发【第二章】:补充
    Python开发【第二章】:深浅拷贝剖析
  • 原文地址:https://www.cnblogs.com/darrenji/p/4517183.html
Copyright © 2011-2022 走看看