11
votes

Covariance et Contravarariance sur le même argument de type

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>

EDIT: stry> 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; 


5 commentaires

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 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).


7 Réponses :


1
votes

Sans ou dans les mots-clés, l'argument est la covariance et la contrevariance n'est-ce pas?

dans signifie que l'argument ne peut être utilisé que comme argument de fonction type

out signifie que l'argument ne peut être utilisé que comme type de valeur de retour

sans in et sortie signifie qu'il peut être utilisé comme type d'argument et que le type de valeur de retour


1 commentaires

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.



8
votes

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.

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.


12 commentaires

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 est qu'il peut être attribué à une variable de type animal . Puisqu'un objet de type cat [] peut être attribué à une variable de type animal [] , le fonctionnement [] (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 n'est pas un sous-type de ienumerator parce que le premier n'est pas dérivé de ce dernier).


Mais x [] <: y [] sous Liskov implique que x est identique y , 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 [] partout où une valeur de type animal [] peut être utilisée, des tableaux en C # sont covariants. De même, iEnumerable 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 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 .



5
votes

Covariance est possible pour les types que vous n'entrez jamais (E.G. Les fonctions membres peuvent l'utiliser comme type de retour ou out , 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 ).

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.


0 commentaires

0
votes

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?

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.

covariance signifie s <: t ⇒ g <: g et Contravariance signifie s <: t ⇒ g <: g . Il devrait être assez évident que ceux-ci ne peuvent jamais être vrais en même temps.


3 commentaires

Ce n'est pas évident pour moi. Pourquoi est-il évident que cela peut jamais est vrai? N'est-ce pas le cas qu'il pourrait y avoir des relations définies sur un ensemble tel que g <: g et g >: g ? Ils ne sont peut-être pas des relations utiles ou intéressantes, mais je ne vois pas pourquoi ils sont impossibles .


@ERIC LIPPERT: Vous êtes correct, bien sûr. Dans ce cas spécifique, j'ai pris <: 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 . (Au moins, je ne vois pas comment cela fonctionnerait dans le système de type C #.) En supposant que s et t sont différents types, je ne le vois pas souhaitable d'avoir g et g être le même type.


Eh bien, la relation réelle dans le système de type est Compatibilité d'affectation plutôt que sous-typing ; 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 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 <: g et g :> g mais g ! = G .



25
votes

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>

http://blogs.msdn.com/b/ericlippert/ archives / 2009/12/03 / règles exactes-pour-variance-validity.aspx 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> {}


7 commentaires

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 .


@ Sciences LIPPERT: Je sais ce que tu veux dire. Mon partenaire me dit constamment "Je suis content vous Trouvez-le drôle" après avoir livré une blague à elle.



0
votes

ce que vous pouvez faire avec "covariant"? strong>

covariant utilise le modificateur 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>

Supposons que vous ayez ces classes et interface: p> xxx pré>

supposons maintenant que vous disposiez des types 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>

Lorsque vous faites ceci (un classique CO forte > Affectation de la variante): p> xxx pré>

  • bigOuttter.getanInstance () code> retournera un tbig code> li>
  • et parce que SmallOutTeur code> a été attribué à bigOuttter code>:
    • en interne, smallOuttter.getaninstance () code> retournera tbig code> li>
    • et tbig code> peut être converti en TSMALL CODE> LI>
    • La conversion est effectuée et la sortie est TSMALL code>. li> ul> li> ul>

      Si c'était le contraire (comme s'il était Contra Strong> Variante): P>

      //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>
      • et parce que bigOuttter code> a été attribué à smallOutter code>:
        • interne, bigOuttter.getaninstance () code> retournera TSMALL code> li>
        • mais tsmall code> ne peut pas être converti en tbig code> !! li>
        • Cela n'est pas possible. li> ul> li> ul>

          C'est pourquoi les types " CONTR EM> Variant" ne peuvent pas être utilisés comme sortie forts> types p> blockQuote>


          ce que vous pouvez faire avec "Contravariant"? strong> p>

          Après la même idée ci-dessus, Contravariant utilise le modificateur 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>

          Supposons que vous ayez ces classes et interface: 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;
          


0 commentaires

0
votes

Les paramètres de type générique ne peuvent pas être à la fois covariant et contrevariert.

pourquoi? Cela concerne les restrictions que dans et out imposent des modificateurs imposent. Si nous voulions faire un paramètre de type générique de type Covariant et Contravariant, nous disons essentiellement:

  • Aucune des méthodes de notre interface ne renvoie t
  • Aucune des méthodes de notre interface n'accepte T

    qui rendrait essentiellement notre interface générique non générique.

    Je l'ai expliqué en détail sous une autre Question :


0 commentaires