Afwijking in algemene interfaces (C#)
.NET Framework 4 heeft variantieondersteuning geïntroduceerd voor verschillende bestaande algemene interfaces. Variantieondersteuning maakt impliciete conversie mogelijk van klassen die deze interfaces implementeren.
Vanaf .NET Framework 4 zijn de volgende interfaces variant:
IEnumerable<T> (T is covariant)
IEnumerator<T> (T is covariant)
IQueryable<T> (T is covariant)
IGrouping<TKey,TElement> (
TKey
enTElement
zijn covariant)IComparer<T> (T is contravariant)
IEqualityComparer<T> (T is contravariant)
IComparable<T> (T is contravariant)
Vanaf .NET Framework 4.5 zijn de volgende interfaces variant:
IReadOnlyList<T> (T is covariant)
IReadOnlyCollection<T> (T is covariant)
Met covariantie kan een methode een meer afgeleid retourtype hebben dan dat is gedefinieerd door de algemene typeparameter van de interface. Als u de covariantiefunctie wilt illustreren, kunt u de volgende algemene interfaces overwegen: IEnumerable<Object>
en IEnumerable<String>
. De IEnumerable<String>
interface neemt de IEnumerable<Object>
interface niet over. Het String
type neemt echter het Object
type over en in sommige gevallen wilt u mogelijk objecten van deze interfaces aan elkaar toewijzen. Dit wordt weergegeven in het volgende codevoorbeeld.
IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;
In eerdere versies van .NET Framework veroorzaakt deze code een compilatiefout in C# en, indien Option Strict
ingeschakeld, in Visual Basic. Maar nu kunt u in plaats van objects
, zoals wordt weergegeven in het vorige voorbeeld, omdat strings
de IEnumerable<T> interface covariant is.
Met contravariantie kan een methode argumenttypen hebben die minder zijn afgeleid dan die zijn opgegeven door de algemene parameter van de interface. Als u de contravariantie wilt illustreren, gaat u ervan uit dat u een BaseComparer
klasse hebt gemaakt om exemplaren van de BaseClass
klasse te vergelijken. Met de klasse BaseComparer
wordt de IEqualityComparer<BaseClass>
-interface geïmplementeerd. Omdat de IEqualityComparer<T> interface nu contravariant is, kunt u exemplaren BaseComparer
van klassen vergelijken die de BaseClass
klasse overnemen. Dit wordt weergegeven in het volgende codevoorbeeld.
// 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;
}
}
Zie Afwijking gebruiken in interfaces voor algemene verzamelingen (C#) voor meer voorbeelden.
Variantie in algemene interfaces wordt alleen ondersteund voor referentietypen. Waardetypen bieden geen ondersteuning voor variantie. Kan bijvoorbeeld IEnumerable<int>
niet impliciet worden geconverteerd naar IEnumerable<object>
, omdat gehele getallen worden vertegenwoordigd door een waardetype.
IEnumerable<int> integers = new List<int>();
// The following statement generates a compiler error,
// because int is a value type.
// IEnumerable<Object> objects = integers;
Het is ook belangrijk te onthouden dat klassen die variantinterfaces implementeren, nog steeds invariant zijn. Hoewel de covariant-interface IEnumerable<T>bijvoorbeeld List<T> wordt geïmplementeerd, kunt u niet impliciet converteren List<String>
naar List<Object>
. Dit wordt geïllustreerd in het volgende codevoorbeeld.
// 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>();