Varians i allmänna gränssnitt (C#)
.NET Framework 4 introducerade variansstöd för flera befintliga generiska gränssnitt. Avvikelsestöd möjliggör implicit konvertering av klasser som implementerar dessa gränssnitt.
Från och med .NET Framework 4 är följande gränssnitt variant:
IEnumerable<T> (T är covariant)
IEnumerator<T> (T är covariant)
IQueryable<T> (T är covariant)
IGrouping<TKey,TElement> (
TKey
ochTElement
är covariant)IComparer<T> (T är kontravariant)
IEqualityComparer<T> (T är kontravariant)
IComparable<T> (T är kontravariant)
Från och med .NET Framework 4.5 är följande gränssnitt variant:
IReadOnlyList<T> (T är covariant)
IReadOnlyCollection<T> (T är covariant)
Med kovarians kan en metod ha en mer härledd returtyp än den som definieras av den generiska typparametern i gränssnittet. För att illustrera covariance-funktionen bör du överväga dessa allmänna gränssnitt: IEnumerable<Object>
och IEnumerable<String>
. Gränssnittet IEnumerable<String>
ärver IEnumerable<Object>
inte gränssnittet. Men typen String
ärver Object
typen, och i vissa fall kanske du vill tilldela objekt av dessa gränssnitt till varandra. Detta visas i följande kodexempel.
IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;
I tidigare versioner av .NET Framework orsakar den här koden ett kompileringsfel i C# och, om Option Strict
den är på, i Visual Basic. Men nu kan du använda strings
i stället för objects
, som du ser i föregående exempel, eftersom IEnumerable<T> gränssnittet är covariant.
Contravariance tillåter att en metod har argumenttyper som är mindre härledda än den som anges av den generiska parametern i gränssnittet. Anta att du har skapat en BaseComparer
klass för att jämföra instanser av BaseClass
klassen för att illustrera kontravarians. Klassen BaseComparer
implementerar gränssnittet IEqualityComparer<BaseClass>
. IEqualityComparer<T> Eftersom gränssnittet nu är kontravariant kan du använda BaseComparer
för att jämföra instanser av klasser som ärver BaseClass
klassen. Detta visas i följande kodexempel.
// 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;
}
}
Fler exempel finns i Använda varians i gränssnitt för generiska samlingar (C#).
Varians i allmänna gränssnitt stöds endast för referenstyper. Värdetyper stöder inte varians. Det IEnumerable<int>
går till exempel inte att implicit konvertera till IEnumerable<object>
, eftersom heltal representeras av en värdetyp.
IEnumerable<int> integers = new List<int>();
// The following statement generates a compiler error,
// because int is a value type.
// IEnumerable<Object> objects = integers;
Det är också viktigt att komma ihåg att klasser som implementerar variantgränssnitt fortfarande är invarianta. Även om List<T> du till exempel implementerar det covarianta gränssnittet IEnumerable<T>kan du inte implicit konvertera List<String>
till List<Object>
. Detta illustreras i följande kodexempel.
// 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>();