介面(interface)的逆變與協變無法在 C# 3.0 進行編譯
定義一個介面和繼承該介面的兩個類別
internal interface IAnimal
{
string Name { get; set; }
int Age { get; set; }
}
internal class Dog : IAnimal
{
public string Name { get; set; }
public int Age { get; set; }
public string Size { get; set; }
}
internal class Bird : IAnimal
{
public string Name { get; set; }
public int Age { get; set; }
public bool IsFlying { get; set; }
}
實例化兩個List<T>物件
List<Dog> dogs = new List<Dog>
{
new Dog{Name="Dog 1",Age=3, Size="Small"},
new Dog{Name="Dog 2",Age=5, Size="Big"}
};
List<Bird> birds = new List<Bird>
{
new Bird{Name="Bird 1",Age=4,IsFlying=true},
new Bird{Name="Bird 2",Age=2,IsFlying=false}
};
演示 IEnumerable<out T>協變介面
1.
List<IAnimal> animals = new List<IAnimal>();
animals.AddRange(dogs);
animals.AddRange(birds);
建立一個 List<IAnimal>, 並調用 AddRange 向其添加 Dog 和 Bird列表(List);
List<T>.AddRange 的參數為 IEnumerable<T> 類型,
因此這種情況下,將這兩個列表都看成是 IEnumerable<IAnimal>,而這以前是不允許的。
2.
List<IAnimal> concat = dogs.Concat<IAnimal>(birds).ToList();
使用LINQ方法,根據已知序列的數據創建列表;
不能直接調用 dogs.Concat(birds),這會使類型推斷機制變得混亂, 應該顯示地指定類型參數。
dogs 和 cats 都將根據協變性而隱式轉換為 IEnumerable<IAnimal>, 這種轉換不會真正改變它們的值,所改變的只是編譯器如何看待這些值。
結果:
foreach (var item in animals)
{
Console.WriteLine(item.Name);
}
顯示 :
Dog 1
Dog 2
Bird 1
Bird 2
無法使用 item.Size 或是 item.IsFlying, 除非轉為 Dog 或是 Bird:
foreach (var item in animal)
{
if (item is Bird)
{
var bird = (Bird)item;
Console.WriteLine("the bird is flying? {0}", bird.IsFlying);
}
}
演示 IComparer<in T> 逆變介面
internal class AgeComparer : IComparer<IAnimal>
{
public int Compare(IAnimal x, IAnimal y)
{
return x.Age.CompareTo(y.Age);
}
}
自定 AgeComparer 類別,實現 IComparer<T>
....
IComparer<IAnimal> ageComparer = new AgeComparer();
dogs.Sort(ageComparer);
使用逆變性,進行排序;有了 IComparer<IAnimal>,就可以用她進行排序,dogs.Sort 的參數應該為 IComparer<Dog> 類型,但逆變性會進行隱式轉換。