先看個例子,此代碼在c# 4.0下可以編譯通過,因為c#4.0才開始支援逆變和協變
代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
public static void Main()
{
IEnumerable<object> objs = new List<string>(); //協變
Action<object> action1 = o => { };
Action<string> action2 = action1;//逆變
}
}
}
下面是泛型介面IEnumerable<T>相互關聯類型的定義
代碼
public interface IEnumerable<out T> : IEnumerable
{
// Summary:
// Returns an enumerator that iterates through the collection.
//
// Returns:
// A System.Collections.Generic.IEnumerator<T> that can be used to iterate through
// the collection.
IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<out T> : IDisposable, IEnumerator
{
// Summary:
// Gets the element in the collection at the current position of the enumerator.
//
// Returns:
// The element in the collection at the current position of the enumerator.
T Current { get; }
}
可以看到T在IEnumerable定義中比以前多了out的修飾符。out表示T在泛型型別中只能被用作輸出。也就是說T定義的對象是只會被用作賦值給客戶代碼中的引用。這樣凡是使用IEnumerable<BaseType>類型的地方都可以被安全的替換成IEnumerable<SubType>.因為子類型(SubType)可以安全替換父類型(BaseType)
下面是泛型委派Action<T>的定義
public delegate void Action<in T>(T obj);
這裡是用in修飾T。說明T在泛型中只會被用作輸入,也就是說T定義的引用只能用作接受客戶代碼的賦值。所以使用Action<SubType>的地方都可以被安全的替換成Acton<BaseType>.道理和上面一樣子類型(SubType)可以安全替換父類型(BaseType)。
總結:協變和逆變允許我們定義的泛型介面和泛型委派能更加的通用。而且保證不會破壞型別安全。