Partager via


Variance dans les interfaces génériques (C#)

.NET Framework 4 a introduit la prise en charge des variances pour plusieurs interfaces génériques existantes. La prise en charge de la variance permet la conversion implicite de classes qui implémentent ces interfaces.

À compter de .NET Framework 4, les interfaces suivantes sont variant :

À compter de .NET Framework 4.5, les interfaces suivantes sont des variantes :

La covariance permet à une méthode d’avoir un type de retour plus dérivé que celui défini par le paramètre de type générique de l’interface. Pour illustrer la fonctionnalité de covariance, tenez compte des interfaces génériques suivantes : IEnumerable<Object> et IEnumerable<String>. L’interface IEnumerable<String> n’hérite pas de l’interface IEnumerable<Object> . Toutefois, le String type hérite du Object type et, dans certains cas, vous souhaiterez peut-être affecter des objets de ces interfaces les uns aux autres. Ceci est illustré dans l’exemple de code suivant.

IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;

Dans les versions antérieures de .NET Framework, ce code provoque une erreur de compilation en C# et, le cas Option Strict échéant, en Visual Basic. Mais maintenant, vous pouvez utiliser strings au lieu de objects, comme indiqué dans l’exemple précédent, car l’interface IEnumerable<T> est covariante.

La contravariance permet à une méthode d’avoir des types d’arguments moins dérivés que ceux spécifiés par le paramètre générique de l’interface. Pour illustrer la contravariance, supposons que vous avez créé une BaseComparer classe pour comparer les instances de la BaseClass classe. La classe BaseComparer implémente l’interface IEqualityComparer<BaseClass>. Étant donné que l’interface IEqualityComparer<T> est désormais contravariante, vous pouvez utiliser BaseComparer pour comparer des instances de classes qui héritent de la BaseClass classe. Ceci est illustré dans l’exemple de code suivant.

// 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;
    }
}

Pour plus d’exemples, consultez Utilisation de la variance dans les interfaces pour les collections génériques (C#).

La variance dans les interfaces génériques est prise en charge uniquement pour les types de référence. Les types valeur ne prennent pas en charge la variance. Par exemple, IEnumerable<int> ne peut pas être converti implicitement en IEnumerable<object>, car les entiers sont représentés par un type valeur.

IEnumerable<int> integers = new List<int>();
// The following statement generates a compiler error,
// because int is a value type.
// IEnumerable<Object> objects = integers;

Il est également important de se rappeler que les classes qui implémentent des interfaces variant sont toujours invariantes. Par exemple, bien qu’implémente List<T> l’interface IEnumerable<T>covariante, vous ne pouvez pas convertir List<String> implicitement en List<Object>. Ceci est illustré dans l’exemple de code suivant.

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

Voir aussi