泛型介面中的差異 (C#)
.NET Framework 4 針對數個現有泛型介面推出了差異支援。 差異支援可讓實作這些介面的類別以隱含方式轉換。
開始使用 .NET Framework 4.6,下列介面是 Variant:
IEnumerable<T> (T 是 Covariant)
IEnumerator<T> (T 是 Covariant)
IQueryable<T> (T 是 Covariant)
IGrouping<TKey,TElement> (
TKey
和TElement
都是 Covariant)IComparer<T> (T 是 Contravariant)
IEqualityComparer<T> (T 是 Contravariant)
IComparable<T> (T 是 Contravariant)
開始使用 .NET Framework 4.5,下列介面是變數:
IReadOnlyList<T> (T 是 Covariant)
IReadOnlyCollection<T> (T 是 Covariant)
共變數允許方法具有比介面泛型型別參數所定義之衍生程度更大的傳回型別。 若要示範共變數功能,請考慮這些泛型介面︰IEnumerable<Object>
和 IEnumerable<String>
。 IEnumerable<String>
介面不會繼承 IEnumerable<Object>
介面。 不過,String
型別確實會繼承Object
型別,而且在某些情況下,您可能希望將這些介面的物件指派給彼此。 這會顯示在以下程式碼範例中。
IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;
在舊版的 .NET Framework 中,此程式碼會造成 C# 和 Visual Basic (如果開啟 Option Strict
) 中的編譯錯誤。 但現在您可以使用 strings
,而不使用 objects
,如上例所示,因為 IEnumerable<T> 介面是 Covariant。
反變數允許方法具有比介面泛型參數所指定之衍生程度更小的引數型別。 為示範反變數,我們假設您已建立 BaseComparer
類別來比較 BaseClass
類別的執行個體。 BaseComparer
類別會實作 IEqualityComparer<BaseClass>
介面。 因為 IEqualityComparer<T> 介面現在是 Contravariant,所以您可以使用 BaseComparer
比較繼承了 BaseClass
類別的類別執行個體。 這會顯示在以下程式碼範例中。
// Simple hierarchy of classes.
class BaseClass { }
class DerivedClass : BaseClass { }
// Comparer class.
class BaseComparer : IEqualityComparer<BaseClass>
{
public int GetHashCode(BaseClass baseInstance)
{
return baseInstance.GetHashCode();
}
public bool Equals(BaseClass x, BaseClass y)
{
return x == y;
}
}
class Program
{
static void Test()
{
IEqualityComparer<BaseClass> baseComparer = new BaseComparer();
// Implicit conversion of IEqualityComparer<BaseClass> to
// IEqualityComparer<DerivedClass>.
IEqualityComparer<DerivedClass> childComparer = baseComparer;
}
}
如需更多範例,請參閱針對泛型集合使用介面中的差異 (C#)。
僅參考型別支援泛型介面的差異。 實值型別不支援差異。 例如,IEnumerable<int>
無法以隱含方式轉換成 IEnumerable<object>
,因為整數是以實值型別表示。
IEnumerable<int> integers = new List<int>();
// The following statement generates a compiler error,
// because int is a value type.
// IEnumerable<Object> objects = integers;
請務必牢記,實作 Variant 介面的類別仍然是非變異的。 例如,雖然 List<T> 實作了 Covariant 介面 IEnumerable<T>,但您不能以隱含方式將 List<String>
轉換成 List<Object>
。 下列程式碼範例說明此情形。
// The following line generates a compiler error
// because classes are invariant.
// List<Object> list = new List<String>();
// You can use the interface object instead.
IEnumerable<Object> listObjects = new List<String>();