關於協變逆變,SolidMango的解釋是比較可取的。有了協變,比如,在需要返回IEnumerable<object>類型的時候,可以使用IEnmerable<string>來替代;有了逆變,比如,在需要接收IComparable<string>類型形參方法中,可以使用IComparable<obj ...
關於協變逆變,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();
可見,在方法中基於派生類介面類型的形參,調用該方法的時候可以傳入基類介面類型的實參。
總結:在本篇的場景中,派生類介面替代父類介面,稱之為協變;父類介面代替派生類介面,稱之為逆變。