Dela via


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:

Från och med .NET Framework 4.5 är följande gränssnitt variant:

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>();

Se även