Rozptyl v obecných rozhraních (C#)
Rozhraní .NET Framework 4 zavedlo podporu odchylek pro několik existujících obecných rozhraní. Podpora rozptylu umožňuje implicitní převod tříd, které implementují tato rozhraní.
Počínaje rozhraním .NET Framework 4 jsou následující rozhraní variantou:
IEnumerable<T> (T je kovariantní)
IEnumerator<T> (T je kovariantní)
IQueryable<T> (T je kovariantní)
IGrouping<TKey,TElement> (
TKey
aTElement
jsou kovariantní)IComparer<T> (T je kontravariantní)
IEqualityComparer<T> (T je kontravariantní)
IComparable<T> (T je kontravariantní)
Počínaje rozhraním .NET Framework 4.5 jsou varianta následujících rozhraní:
IReadOnlyList<T> (T je kovariantní)
IReadOnlyCollection<T> (T je kovariantní)
Kovariance umožňuje metodě mít odvozenější návratový typ, než který definuje parametr obecného typu rozhraní. Chcete-li ilustrovat kovarianci funkce, zvažte tato obecná rozhraní: IEnumerable<Object>
a IEnumerable<String>
. Rozhraní IEnumerable<String>
nedědí IEnumerable<Object>
rozhraní. String
Typ však zdědí Object
typ a v některých případech můžete chtít přiřadit objekty těchto rozhraní k sobě navzájem. To je znázorněno v následujícím příkladu kódu.
IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;
V dřívějších verzích rozhraní .NET Framework tento kód způsobí chybu kompilace v jazyce C# a pokud Option Strict
je zapnutá, v jazyce Visual Basic. Nyní ale můžete místo strings
objects
, jak je znázorněno v předchozím příkladu, protože IEnumerable<T> rozhraní je kovariantní.
Kontravariance umožňuje metodě mít typy argumentů, které jsou méně odvozeny, než které specifikuje obecný parametr rozhraní. Chcete-li ilustrovat kontravariance, předpokládejme, že jste vytvořili BaseComparer
třídu pro porovnání instancí BaseClass
třídy. Třída BaseComparer
implementuje rozhraní IEqualityComparer<BaseClass>
. Vzhledem k tomu, že IEqualityComparer<T> rozhraní je nyní kontravariantní, můžete použít BaseComparer
k porovnání instancí tříd, které dědí BaseClass
třídu. To je znázorněno v následujícím příkladu kódu.
// 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;
}
}
Další příklady najdete v tématu Použití rozptylu v rozhraních pro obecné kolekce (C#).
Variance v obecných rozhraních je podporována pouze pro odkazové typy. Typy hodnot nepodporují odchylku. Například IEnumerable<int>
nelze implicitně převést na IEnumerable<object>
, protože celá čísla jsou reprezentována typem hodnoty.
IEnumerable<int> integers = new List<int>();
// The following statement generates a compiler error,
// because int is a value type.
// IEnumerable<Object> objects = integers;
Je také důležité si uvědomit, že třídy, které implementují variantní rozhraní, jsou stále invariantní. Ačkoli například List<T> implementuje kovariantní rozhraní IEnumerable<T>, nelze implicitně převést List<String>
na List<Object>
. To je znázorněno v následujícím příkladu kódu.
// 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>();