這個做法提出了三個概念: 共變數(Covariance)、反變數(Contravariance)與不變數(Invariant)。
直接閱讀程式碼比較容易理解,從英文來看比較容易理解 less derived type
代表繼承中較低位階的類型,
我們知道在 C# 中所有類型都是從 Object
衍生出來的,所以在這個例子中 Object
跟其它類型比起來就是一個 less derived type
。
more derived type
則是高位階類型例如 string
類型就是從 Object
衍生出來的。
這個就是類型間相容性的概念。
void Main()
{
// Assignment compatibility.
string str = "test";
// An object of a more derived type is assigned to an object of a less derived type.
object obj = str;
}
但是下面這種寫法看似正確但結果是會編譯錯誤,這是因為泛型類型沒有特別指定的話都是 Invariant,代表傳遞給該參數的變數型別必須跟宣告的型別完全一樣。
void Main()
{
List<string> strings = new List<string>();
List<object> objects = strings;
}
所以在新版的 C# 引入了 Covariance 的概念,例如下面這段程式碼的寫法可以把 List<string>
賦值給 IEnumerable<string>
型別,
也就是把高位階的類型賦值給低位階的類型。
void Main()
{
// Covariance.
IEnumerable<string> strings = new List<string>();
// An object that is instantiated with a more derived type argument
// is assigned to an object instantiated with a less derived type argument.
// Assignment compatibility is preserved.
IEnumerable<object> objects = strings;
}
能做到這樣的效果都是靠新的參數 in
與 out
,所以上面用到的 IEnumerable 內部就是加上了 out
參數進行修飾,才能獲得 Covariance 的功能。
public interface IEnumerable<out T> : IEnumerable
Contravariance 則是相反的概念,像下面這段程式碼就是把 Action<object>
賦值給 Action<string>
,也就是低位階的類型賦值給高位階的類型。
public class Contravariance
{
// Contravariance.
// Assume that the following method is in the class:
static void SetObject(object o) { }
public void Create()
{
Action<object> actObject = SetObject;
// An object that is instantiated with a less derived type argument
// is assigned to an object instantiated with a more derived type argument.
// Assignment compatibility is reversed.
Action<string> actString = actObject;
}
}
上面用到的 Action 內部就是加上了 in
參數進行修飾,才能獲得 Contravariance 的功能。
public delegate void Action<in T>(T obj);
Summary
這個做法提到了泛型會被視為 Invariant,所以現在建議是加上 in
與 out
來修飾介面和委派讓我們的類型獲得 Covariance 與 Contravariance
的功能。