La spécification C # indique qu'un type d'argument ne peut pas être à la fois covariant et contravariant en même temps.
Ceci est évident lors de la création d'une interface covariante ou contravariante, vous décorez vos paramètres de type avec «OUT» ou «IN». Il n'y a pas d'option qui permet à la fois en même temps ("Outtin"). P>
Est-ce que cette limitation est simplement une contrainte spécifique de la langue ou existe des raisons plus profondes, plus fondamentales basées dans la théorie de la catégorie qui vous ferait ne pas vouloir votre type sera à la fois covariant et contrevariert? p>
Ma compréhension était que les tableaux étaient réellement covariants et contravariants. P> public class Pet{}
public class Cat : Pet{}
public class Siamese : Cat{}
Cat[] cats = new Cat[10];
Pet[] pets = new Pet[10];
Siamese[] siameseCats = new Siamese[10];
//Cat array is covariant
pets = cats;
//Cat array is also contravariant since it accepts conversions from wider types
cats = siameseCats;
7 Réponses :
Sans ou dans les mots-clés, l'argument est la covariance et la contrevariance n'est-ce pas? P>
dans strong> signifie que l'argument ne peut être utilisé que comme argument de fonction type p>
out fort> signifie que l'argument ne peut être utilisé que comme type de valeur de retour p>
sans in et sortie signifie qu'il peut être utilisé comme type d'argument et que le type de valeur de retour p>
Dans la mesure où l'intersection de la covariance et de la contrevenance est l'invariance, oui. Je pense que l'OP veut l'union de la covariance et de la contrevérance, et que ce n'est tout simplement pas possible.
Covariance et Contravariance sont mutuellement exclusives. Votre question est que vous posez de vous demander si le réglage A peut être à la fois un superset de l'ensemble B et un sous-ensemble de l'ensemble B. Pour que défini A soit à la fois un sous-ensemble et une superset de l'ensemble B, définissez A doit être égal à Set B, donc alors vous voudriez simplement demander si le réglage A est égal à fixer b. p>
En d'autres termes, demander de la covariance et de la contrevenance sur le même argument est que de ne demander aucune variance du tout (invariance), qui est la valeur par défaut. Ainsi, il n'est pas nécessaire de le spécifier. P>
Umm, c'est possible, cela nécessite que A est identique à B. En d'autres termes, invariance.
Ben: Merci, j'ai clarifié, j'espère.
Je pensais que l'invariance était quand ni la covariance ni la contrevenance ne s'appliquaient.
De plus, des tableaux ne sont-ils pas à la fois covariants et contravariants? Si oui, les deux ne sont pas mutuellement exclusives car la fonctionnalité existe réellement à l'état sauvage.
@William: Les tableaux sont strictement covariants. L'invariance est l'intersection de la covariance et de la contrevenance.
@BEN: La variance est une propriété d'opérations sur des types, pas des objets. Une propriété de type CAT code> est qu'il peut être attribué à une variable de type
animal code>. Puisqu'un objet de type
cat [] code> peut être attribué à une variable de type
animal [] code>, le fonctionnement
[] code> (tableau) a le propriété de covariance.
@Ben: Veuillez définir "Covariant" et "Subtype" comme vous les utilisez, car vos définitions sont clairement différentes de celles utilisées par le reste de nous. Ma définition de "covariant" n'a rien à voir avec la sécurité ou les sous-types de type, et ma définition de "sous-type" est "dérive de" (par exemple, ienumerator
ienumerator
Mais x [] <: y [] code> sous Liskov implique que
x code> est identique
y code>, d'où la relation réseau est invariante.
Ben: Bien qu'il soit parfaitement valable d'utiliser "Liskov substituable" pour votre définition de covariance, il n'est pas très utile de la discussion sur la discussion sur C #. Le vérificateur de type C # sait rien de Liskov, de sorte que ses propres règles de substitution déterminent la variance. Étant donné que le compilateur C # vous permettra d'utiliser une valeur de type cat [] code> partout où une valeur de type
animal [] code> peut être utilisée, des tableaux en C # sont covariants. De même,
iEnumerable code> est devenu covariant uniquement en C # 4 car c'est à ce moment-là que l'annotation de type a été ajoutée à l'interface.
Ben: Je ne peux pas croire que nous lisons tous deux la même langue. Au début de votre lien Eric States "des tableaux où le type d'élément est un type de référence est covariant". Cela ne dit rien de prétendre ou de ne pas vraiment être. C'est fait i> dire que ce type de covariance particulier est "cassé" (parce que ce n'est pas typique), mais c'est loin d'être invariant.
@Gabe: Je pense que nous répétons l'ensemble des commentaires sur ce billet de blog. En parlant de quoi, êtes-vous le même gabe qui a commenté là-bas? En tout état de cause, je pense que nous sommes tous deux convenir que la relation de tableau est C # -Covariandt (qui n'est pas vraiment typographe) mais Liskov-invariant. Donc, je propose que, pour le bénéfice des futurs lecteurs, vous reformulez votre deuxième commentaire (n ° 5 dans la séquence) pour mentionner que c # permet à COVARYCE, mais (en prenant note que la question posée sur la théorie), les tableaux sont en réalité liskov- invariant si l'utilisation de Covariant est cassée / unttypesafe - et ensuite nous nuke ce désordre intermédiaire
BTW: les ingénieurs de conception C # aussi Discutez de la covariance en C # dans considérable profondeur , y compris La covariance cassée de tableaux .
Covariance est possible pour les types que vous n'entrez jamais (E.G. Les fonctions membres peuvent l'utiliser comme type de retour ou Si vous avez effectué un paramètre de type Covariant et Contravariant, vous ne pouvez pas le saisir et vous ne pouviez pas le sortir - vous ne pouviez pas l'utiliser du tout. P> out code>, mais jamais comme paramètre d'entrée). Contravariance est possible pour les types que vous ne produisez jamais (par exemple comme paramètre d'entrée, mais jamais comme un type de retour ou un paramètre
code>). P>
Cette limitation est-elle simplement une contrainte spécifique linguistique ou existe-t-il des raisons plus profondes et plus fondamentales basées dans la théorie de la catégorie qui vous ferait ne pas vouloir que votre type soit à la fois covariant et contravaret? P> blockQuote>
Non, il y a une raison beaucoup plus simple basée sur la logique de base (ou simplement le bon sens, quelle que soit votre préférence): une déclaration ne peut être à la fois vraie et non vraie en même temps. P>
covariance signifie
s <: t ⇒ g
<: gCODE> et Contravariance signifie s <: t ⇒ g
<: g code>. Il devrait être assez évident que ceux-ci ne peuvent jamais être vrais en même temps. P>
Ce n'est pas évident pour moi. Pourquoi est-il évident que cela peut jamais i> est vrai? N'est-ce pas le cas qu'il pourrait y avoir des relations définies sur un ensemble tel que g <: g >: g
@ERIC LIPPERT: Vous êtes correct, bien sûr. Dans ce cas spécifique, j'ai pris <: code> pour être la relation de sous-type habituelle. Un type ne peut pas être à la fois un super-espèce et un sous-type d'un autre type à moins qu'ils ne soient le type même i>. (Au moins, je ne vois pas comment cela fonctionnerait dans le système de type C #.) En supposant que
s code> et
t code> sont différents types, je ne le vois pas souhaitable d'avoir
g
code> et g
Eh bien, la relation réelle dans le système de type est Compatibilité d'affectation i> plutôt que sous-typing i>; Il y a des différences subtiles. Dans le système de type CLR, INT et UINT ne sont pas des sous-types les uns des autres, mais ils sont compatibles les uns avec les autres. Parce que les matrices sont covariennes dans le système de type CLR, INT [] et UINT [] sont Aussi i> attribution compatible avec l'autre, même si elles ne sont pas des sous-types non plus. Dans ce cas particulier, où g est "faire un type de tableau", il arrive tellement que g
Comme d'autres l'ont dit, il est logiquement incompatible pour un type générique pour être à la fois covariant et contravariant. Il y a quelques excellentes réponses ici jusqu'à présent, mais permettez-moi d'ajouter deux autres
Tout d'abord, lisez mon article sur le sujet de la variance « validité ». P>
par définition, si un type est "covariante valide" il est non utilisable une façon contravariante em>. Si elle est « contravariantly valide » alors il est pas utilisable de manière covariante em>. Quelque chose qui est les em> covariante valides et valides contravariantly est non utilisables soit une manière covariant ou contravariant em>. Autrement dit, il est invariant em>. Donc, il y a em> l'union de covariant et contravariant:. Leur union est invariant em> p> Ensuite, supposons que pour un instant que vous avez obtenu votre souhait et qu'il y avait une annotation de type qui a travaillé la façon dont je pense que vous voulez: p> interface IBurger<in and out T> {}
L'exemple Iburger est exactement ce que je cherchais.
En ce qui concerne «Pouvez-vous expliquer pourquoi vous les croyez incorrectement d'être contravariatant» après l'avoir dirigé, il n'est évidemment pas contravelant.
Eric: Je pense toujours que l'invariance est l'intersection, pas l'union, de la covariance et de la contrevenance. Mais peut-être que je pense à l'ensemble de types pouvant être utilisés dans un contexte de covariant ou de contravariation, plutôt que de se mettre en jeu de types génériques qui agissent de manière coovarienne ou contre-courant.
Je conviendrais qu'un type à la fois valide et invariant est valide et invariant ne serait pas invariant - il serait inutilisable en tant que type dans l'interface. Je peux penser aux cas où la covariance ou la cravariance pourrait être utile dans une interface de marqueur, mais car les interfaces peuvent dériver de plusieurs interfaces, je ne peux pas penser à un cas où une variance arbitraire serait utile. Si une interface générique peut être transmise à une routine qui ne se soucierait pas du type générique, elle devrait dériver d'une version non générique et la routine doit l'utiliser comme type d'argument.
@ERIC LIPPERT: J'ai trouvé cette réponse après avoir répondu à ce question . La seule chose que je veux dire est que je reçois et j'apprécie votre blague de burger in-nd.
@Jason: Heureux que vous ayez aimé cela, bien que je vous dise, c'est une bonne chose que la fonction principale de mon sens de l'humour est d'amuser moi i>.
@ Sciences LIPPERT: Je sais ce que tu veux dire. Mon partenaire me dit constamment "Je suis content
ce que vous pouvez faire avec "covariant"? strong> covariant utilise le modificateur Supposons que vous ayez ces classes et interface: p> supposons maintenant que vous disposiez des types Lorsque vous faites ceci (un classique Si c'était le contraire (comme s'il était Contra Strong> Variante): P> C'est pourquoi les types ce que vous pouvez faire avec "Contravariant"? strong> p> Après la même idée ci-dessus, Contravariant utilise le modificateur Supposons que vous ayez ces classes et interface: p> out code>, ce qui signifie que le type peut être une sortie d'une méthode, mais pas un paramètre d'entrée. p>
TBIG code> Inhéting
TSMALL . Cela signifie qu'un
tbig code> instance est toujours une instance code> TSMALL code>; Mais une instance code> tsmall code> n'est pas toujours une instance code> tbig code>. (Les noms ont été choisis pour être faciles à visualiser
TSMALL code> montage intérieur
tbig code>) p>
bigOuttter.getanInstance () code> retournera un
tbig code> li>
SmallOutTeur code> a été attribué à
bigOuttter code>:
smallOuttter.getaninstance () code> retournera
tbig code> li>
tbig code> peut être converti en
TSMALL CODE> LI>
TSMALL code>. li>
ul> li>
ul>
//a real instance that can use TBig methods
Analyser<TBig> bigAnalyser = new Analyser<TBig>();
//this means that TBig has amICool, but not necessarily that TSmall has it
//just a view of bigAnalyser
ICanInput<TSmall> smallAnalyser = bigAnalyser;
smallOuttter.getaninstance () code> retournera
tsmall code> li>
bigOuttter code> a été attribué à
smallOutter code>:
bigOuttter.getaninstance () code> retournera
TSMALL code> li>
tsmall code> ne peut pas être converti en
tbig code> !! li>
dans code >, ce qui signifie que le type peut être un paramètre d'entrée d'une méthode, mais pas un paramètre de sortie. P>
//a real instance that can use TSmall methods
Analyser<TSmall> smallAnalyser = new Analyser<TSmall>();
//this means that TSmall implements amICool
//just a view of smallAnalyser
ICanInput<TBig> bigAnalyser = smallAnalyser;
Les paramètres de type générique ne peuvent pas être à la fois covariant et contrevariert. P>
pourquoi? Cela concerne les restrictions que qui rendrait essentiellement notre interface générique non générique. P>
Je l'ai expliqué en détail sous une autre Question : P> dans code> et
Je suis confus par votre dernière déclaration; Comment est-ce que c'est une démonstration de contraviarance? "Siamois" est un type plus étroit i> type que "CAT", tout comme "CAT" est un type plus étroit que "PET".
Tous les chats sont des animaux domestiques et tous les chats siamois sont des chats, de sorte que ne démontre que la covariance.
Eric - tu as raison ça n'a aucun sens.
Il y a des situations où il serait utile que les matrices soient contravéribles dans le même sens qu'ils sont covariants, mais pour les écrit plutôt que sur des lectures, c'est-à-dire si une routine s'attend à un tableau dans lequel il peut stocker dérivé, il serait possible pour cela. travailler étant donné un tableau de base. La meilleure approche pour atteindre cela pourrait peut-être avoir été pour que des tableaux puissent mettre en œuvre IreadableByindex, ce qui serait covariable et iwrritable , qui serait contravaret, de sorte que les routines qui n'ont besoin que de lire ou d'écrire un tableau pourraient faire la bonne choix.
Incidemment, on peut écrire une routine pour trier tout objet Array-ISH, sans avoir à connaître son type, si l'objet Array-ISH prend en charge une interface ISORTABLEBYDEX non générique avec des méthodes Compatice (int index1, int index2) et Swapat (INT Index1 , int index2).