240 votes

<out T> vs <T> dans Generics

Quelle est la différence entre <out T> y <T> ? Par exemple :

public interface IExample<out T>
{
    ...
}

vs.

public interface IExample<T>
{
    ...
}

250voto

Reed Copsey Points 315315

En out Le mot clé dans les génériques est utilisé pour indiquer que le type T dans l'interface est covariant. Voir Covariance et contravariance pour les détails.

L'exemple classique est IEnumerable<out T> . Puisque IEnumerable<out T> est covariant, vous êtes autorisé à faire ce qui suit :

IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;

La deuxième ligne ci-dessus échouerait si elle n'était pas covariante, même si logiquement elle devrait fonctionner, puisque string dérive de object. Avant la variance des interfaces génériques a été ajouté à C# et VB.NET (dans .NET 4 avec VS 2010), il s'agissait d'une erreur de compilation.

Après .NET 4, IEnumerable<T> a été marqué covariant, et est devenu IEnumerable<out T> . Puisque IEnumerable<out T> n'utilise que les éléments qu'il contient et ne les ajoute ni ne les modifie jamais. Il peut donc traiter une collection énumérable de chaînes de caractères comme une collection énumérable d'objets. covariant .

Cela ne fonctionnerait pas avec un type comme IList<T> puisque IList<T> a un Add méthode. Supposons que cela soit autorisé :

IList<string> strings = new List<string>();
IList<object> objects = strings;  // NOTE: Fails at compile time

Vous pourriez alors appeler :

objects.Add(new Image()); // This should work, since IList<object> should let us add **any** object

Ce serait, bien sûr, un échec - donc IList<T> ne peut pas être marqué covariant.

Il y a aussi, entre-temps, une option de in - qui est utilisé par des choses comme les interfaces de comparaison. IComparer<in T> par exemple, fonctionne de manière inverse. Vous pouvez utiliser un IComparer<Foo> directement en tant que IComparer<Bar> si Bar est une sous-classe de Foo parce que le IComparer<in T> L'interface est contravariant .

77voto

Pour se souvenir facilement de l'utilisation de in y out (également covariance et contravariance), nous pouvons imaginer l'héritage comme une enveloppe :

String : Object
Bar : Foo

in/out

61voto

Jodrell Points 14205

Considérer,

class Fruit {}

class Banana : Fruit {}

interface ICovariantSkinned<out T> {}

interface ISkinned<T> {}

et les fonctions,

void Peel(ISkinned<Fruit> skinned) { }

void Peel(ICovariantSkinned<Fruit> skinned) { }

La fonction qui accepte ICovariantSkinned<Fruit> sera en mesure d'accepter ICovariantSkinned<Fruit> ou ICovariantSkinned<Banana> parce que ICovariantSkinned<T> est une interface covariante et Banana est un type de Fruit ,

la fonction qui accepte ISkinned<Fruit> ne pourra accepter que ISkinned<Fruit> .

51voto

James World Points 9394

" out T "signifie que le type T est "covariant". Cela restreint T pour n'apparaître que comme valeur retournée (sortante) dans les méthodes de la classe, de l'interface ou de la méthode générique. L'implication est que vous pouvez couler le type/interface/méthode vers un équivalent avec un super-type de T .
Par exemple ICovariant<out Dog> peut être transformé en ICovariant<Animal> .

7voto

Brad Cunningham Points 3835

Du lien que vous avez posté....

Pour les paramètres de type générique, t est covariant .

EDIT : Encore une fois, d'après le lien que vous avez posté

Pour plus d'informations, voir Covariance et Contravariance (C# et Visual Basic). http://msdn.microsoft.com/en-us/library/ee207183.aspx

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X