Quelle est la différence entre <out T>
et <T>
? Par exemple:
public interface IExample<out T>
{
...
}
vs.
public interface IExample<T>
{
...
}
Le mot clé out
dans les génériques indique que le type T de l'interface est covariant. Voir Covariance et contravariance pour plus de détails.
L'exemple classique est IEnumerable<out T>
. Puisque IEnumerable<out T>
est covariant, vous êtes autorisé à effectuer les opérations suivantes:
IEnumerable<string> strings = new List<string>();
IEnumerable<object> objects = strings;
La deuxième ligne ci-dessus échouerait si ce n'était pas une covariante, même si cela devrait fonctionner logiquement, puisque string dérive de object. Avant variance dans les interfaces génériques était 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>
était marqué covariant et est devenu IEnumerable<out T>
. Puisque IEnumerable<out T>
utilise uniquement les éléments qu’il contient, et ne les ajoute jamais/ne les modifie pas, il est prudent de traiter une collection énumérable de chaînes comme une collection énumérable d’objets, ce qui signifie qu’elle est covariante .
Cela ne fonctionnerait pas avec un type comme IList<T>
, puisque IList<T>
a une méthode Add
. Supposons que cela soit autorisé:
IList<string> strings = new List<string>();
IList<object> objects = strings; // NOTE: Fails at compile time
Vous pouvez alors appeler:
objects.Add(new Image()); // This should work, since IList<object> should let us add **any** object
Bien entendu, cela échouerait - alors IList<T>
ne peut pas être marqué comme covariant.
Il y a aussi, en passant, une option pour in
- qui est utilisée par des choses comme les interfaces de comparaison. IComparer<in T>
, par exemple, fonctionne dans le sens opposé. Vous pouvez utiliser un béton IComparer<Foo>
directement comme un IComparer<Bar>
si Bar
est une sous-classe de Foo
, car le IComparer<in T>
interface est contravariant .
Pour mémoriser facilement l'utilisation des mots-clés in
et out
(ainsi que covariance et contravariance), nous pouvons imaginer l'héritage comme enveloppant:
String : Object
Bar : Foo
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>
pourra accepter ICovariantSkinned<Fruit>
ou ICovariantSkinned<Bananna>
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>
.
"out T
"signifie que le type T
est" covariant ". Cela limite le fait que T
n'apparaisse que comme une valeur renvoyée (sortante) dans les méthodes de la classe, de l'interface ou de la méthode générique. vous pouvez convertir le type/interface/method en équivalent avec un super-type de T
.
Par exemple. ICovariant<out Dog>
peut être converti en ICovariant<Animal>
.
Du lien que vous avez posté ....
Pour les paramètres de type générique, , le mot clé out spécifie que le paramètre de type est covariant .
[~ # ~] éditer [~ # ~] : encore une fois, à partir du lien que vous avez posté
Pour plus d'informations, consultez Covariance et contravariance (C # et Visual Basic). http://msdn.Microsoft.com/en-us/library/ee207183.aspx