En C #, le out
mot - clé peut être utilisé de deux manières différentes.
Comme modificateur de paramètre dans lequel un argument est passé par référence
class OutExample { static void Method(out int i) { i = 44; } static void Main() { int value; Method(out value); // value is now 44 } }
En tant que modificateur de paramètre de type pour spécifier la covariance .
// Covariant interface. interface ICovariant<out R> { } // Extending covariant interface. interface IExtCovariant<out R> : ICovariant<R> { } // Implementing covariant interface. class Sample<R> : ICovariant<R> { } class Program { static void Test() { ICovariant<Object> iobj = new Sample<Object>(); ICovariant<String> istr = new Sample<String>(); // You can assign istr to iobj because // the ICovariant interface is covariant. iobj = istr; } }
Ma question est: pourquoi?
Pour un débutant, la connexion entre les deux ne semble pas intuitive . L'utilisation avec des génériques ne semble avoir rien à voir avec le passage par référence.
J'ai d'abord appris ce qui out
était lié au passage d'arguments par référence, ce qui a nui à ma compréhension de l'utilisation de la définition de la covariance avec les génériques.
Existe-t-il un lien entre ces utilisations qui me manque?
c#
terminology
language-design
keywords
Rowan Freeman
la source
la source
System.Func<in T, out TResult>
délégué .Réponses:
Il y a une connexion, mais elle est un peu lâche. En C #, les mots clés «in» et «out» comme leur nom l'indique représentent l'entrée et la sortie. C'est très clair dans le cas des paramètres de sortie, mais moins net ce qu'il a à voir avec les paramètres du modèle.
Jetons un coup d'œil au principe de substitution de Liskov :
Voyez comment la contravariance est associée à l'entrée et la covariance est associée à la sortie? En C # si vous marquez une variable de modèle avec
out
pour la rendre covariante, mais veuillez noter que vous ne pouvez le faire que si le paramètre de type mentionné n'apparaît qu'en sortie (type de retour de fonction). Donc, ce qui suit n'est pas valide:De même, si vous marquez un paramètre de type avec
in
, cela signifie que vous ne pouvez l'utiliser que comme entrée (paramètre de fonction). Donc, ce qui suit n'est pas valide:Donc, pour résumer, la connexion avec le
out
mot-clé est qu'avec les paramètres de fonction, cela signifie qu'il s'agit d'un paramètre de sortie , et pour les paramètres de type, cela signifie que le type n'est utilisé que dans le contexte de sortie .System.Func
est également un bon exemple de ce que rwong a mentionné dans son commentaire. DansSystem.Func
tous les paramètres d'entrée sont flagués avecin
, et le paramètre de sortie est flagué avecout
. La raison est exactement ce que j'ai décrit.la source
@ Gábor a déjà expliqué le lien (contravariance pour tout ce qui entre ", covariance pour tout ce qui sort"), mais pourquoi réutiliser des mots clés?
Eh bien, les mots clés sont très chers. Vous ne pouvez pas les utiliser comme identifiants dans vos programmes. Mais il n'y a que tant de mots dans la langue anglaise. Ainsi, parfois vous rencontrez des conflits et vous devez renommer maladroitement vos variables, méthodes, champs, propriétés, classes, interfaces ou structures pour éviter de vous heurter à un mot clé. Par exemple, si vous modélisez une école, comment appelez-vous une classe? Vous ne pouvez pas l'appeler une classe, car
class
c'est un mot-clé!Ajouter un mot-clé à une langue est encore plus cher. Il rend fondamentalement illégal tout le code qui utilise ce mot-clé comme identifiant, brisant la compatibilité descendante partout.
Les mots clés
in
etout
existaient déjà, ils pouvaient donc être réutilisés.Ils auraient pu ajouter des mots clés contextuels qui ne sont que des mots clés dans le contexte d'une liste de paramètres de type, mais quels mots clés auraient-ils choisis?
covariant
etcontravariant
?+
et-
(comme Scala, par exemple)?super
etextends
comme Java l'a fait? Pourriez-vous vous souvenir du haut de votre tête quels paramètres sont covariants et contravariants?Avec la solution actuelle, il existe un joli mnémonique: les paramètres de type de sortie obtiennent le
out
mot - clé, les paramètres de type d'entrée obtiennent lein
mot - clé. Notez la symétrie sympa avec les paramètres de méthode: les paramètres de sortie obtiennent leout
mot - clé, les paramètres d'entrée obtiennent lein
mot - clé (enfin, en fait, pas de mot-clé du tout, puisque l'entrée est la valeur par défaut, mais vous avez l'idée).[Remarque: si vous regardez l'historique des modifications, vous verrez que j'ai en fait inversé les deux dans ma phrase d'introduction. Et j'ai même eu un vote positif pendant cette période! Cela vous montre à quel point ce mnémonique est vraiment important.]
la source
interface Accepter<in T> { void Accept(T it);};
, unAccepter<Foo<T>>
accepteraT
comme paramètre d'entrée s'il l'Foo<T>
accepte comme paramètre de sortie, et vice versa. Ainsi, la contre- variance. En revanche,interface ISupplier<out T> { T get();};
unSupplier<Foo<T>>
aura n'importe quelle sorte de varianceFoo
- donc une co- variance.