=============
MISE À JOUR: J'ai utilisé cette réponse comme base pour cette entrée de blog:
Pourquoi les paramètres ref et out ne permettent-ils pas la variation de type?
Consultez la page du blog pour plus de commentaires sur ce problème. Merci pour la bonne question.
=============
Supposons que vous avez des classes Animal
, Mammal
, Reptile
, Giraffe
, Turtle
et Tiger
, avec les relations de sous - classement évidentes.
Supposons maintenant que vous ayez une méthode void M(ref Mammal m)
. M
peut à la fois lire et écrire m
.
Pouvez-vous passer une variable de type Animal
à M
?
Non. Cette variable peut contenir un Turtle
, mais M
supposera qu'elle ne contient que des mammifères. A Turtle
n'est pas un Mammal
.
Conclusion 1 : les ref
paramètres ne peuvent pas être «agrandis». (Il y a plus d'animaux que de mammifères, donc la variable devient "plus grosse" car elle peut contenir plus de choses.)
Pouvez-vous passer une variable de type Giraffe
à M
?
Non. M
Peut écrire m
et M
souhaitera peut-être écrire un Tiger
dans m
. Vous avez maintenant mis a Tiger
dans une variable qui est en fait de type Giraffe
.
Conclusion 2 : les ref
paramètres ne peuvent pas être rendus "plus petits".
Considérez maintenant N(out Mammal n)
.
Pouvez-vous passer une variable de type Giraffe
à N
?
Non. N
Peut écrire n
et N
souhaitera peut-être écrire un fichier Tiger
.
Conclusion 3 : les out
paramètres ne peuvent pas être «plus petits».
Pouvez-vous passer une variable de type Animal
à N
?
Hmm.
Eh bien pourquoi pas? N
ne peut pas lire n
, il ne peut qu'écrire, non? Vous écrivez un Tiger
dans une variable de type Animal
et vous êtes tous ensemble, non?
Faux. La règle n'est pas " N
peut seulement écrire n
".
Les règles sont, brièvement:
1) N
doit écrire n
avant de N
retourner normalement. (En cas de N
lancers, tous les paris sont ouverts.)
2) N
doit écrire quelque chose dans n
avant de lire quelque chose à partir de n
.
Cela permet cette séquence d'événements:
- Déclarez un champ
x
de type Animal
.
- Passez
x
en out
paramètre à N
.
N
écrit un Tiger
dans n
, qui est un alias pour x
.
- Sur un autre fil, quelqu'un écrit un
Turtle
dansx
.
N
tente de lire le contenu de n
, et découvre un Turtle
dans ce qu'il pense être une variable de type Mammal
.
De toute évidence, nous voulons rendre cela illégal.
Conclusion 4 : les out
paramètres ne peuvent pas être rendus "plus grands".
Conclusion finale : ni ref
niout
les paramètres peuvent varier leurs types. Faire autrement, c'est briser la sécurité de type vérifiable.
Si ces problèmes de théorie de base des types vous intéressent, envisagez de lire ma série sur le fonctionnement de la covariance et de la contravariance en C # 4.0 .
out
paramètres ne peuvent-ils pas être «plus grands»? La séquence que vous avez décrite peut être appliquée à n'importe quelle variable, pas seulement à une variable deout
paramètre. Et aussi le lecteur doit convertir la valeur de l'argumentMammal
avant de tenter d'y accéder carMammal
et bien sûr, il peut échouer s'il n'est pas attentionnéParce que dans les deux cas, vous devez pouvoir attribuer une valeur au paramètre ref / out.
Si vous essayez de passer b dans la méthode Foo2 comme référence, et dans Foo2 vous essayez d'assigner a = new A (), ce serait invalide.
Même raison pour laquelle vous ne pouvez pas écrire:
la source
Vous êtes aux prises avec le problème classique de la POO de la covariance (et de la contravariance), voir wikipedia : bien que ce fait puisse défier les attentes intuitives, il est mathématiquement impossible d'autoriser la substitution des classes dérivées au lieu des classes de base pour des arguments mutables (assignables) (et également des conteneurs dont les articles sont assignables, pour la même raison) tout en respectant le principe de Liskov . Pourquoi il en est ainsi est esquissé dans les réponses existantes, et exploré plus en profondeur dans ces articles wiki et les liens qui en découlent.
Les langages POO qui semblent le faire tout en restant traditionnellement sécurisés statiquement sont la «triche» (insertion de vérifications de type dynamiques cachées, ou exigeant un examen à la compilation de TOUTES les sources à vérifier); le choix fondamental est: soit abandonner cette covariance et accepter la perplexité des praticiens (comme le fait C # ici), soit passer à une approche de typage dynamique (comme l'a fait le tout premier langage POO, Smalltalk), ou passer à immuable (simple- affectation), comme le font les langages fonctionnels (sous immuabilité, vous pouvez prendre en charge la covariance, et également éviter d'autres énigmes connexes telles que le fait que vous ne pouvez pas avoir de sous-classe Square Rectangle dans un monde de données mutables).
la source
Considérer:
Cela violerait la sécurité de type
la source
var
c'était totalement faux. Fixé.Alors que les autres réponses ont brièvement expliqué le raisonnement derrière ce comportement, je pense qu'il vaut la peine de mentionner que si vous avez vraiment besoin de faire quelque chose de cette nature, vous pouvez accomplir des fonctionnalités similaires en faisant de Foo2 une méthode générique, en tant que telle:
la source
Parce que donner
Foo2
unref B
aurait pour résultat un objet mal formé parceFoo2
que ne sait comment remplirA
qu'une partie deB
.la source
N'est-ce pas le compilateur qui vous dit qu'il aimerait que vous lanciez explicitement l'objet afin qu'il puisse être sûr que vous savez quelles sont vos intentions?
la source
Cela a du sens du point de vue de la sécurité, mais j'aurais préféré que le compilateur donne un avertissement au lieu d'une erreur, car il existe des utilisations légitimes d'objets polymoprhiques passés par référence. par exemple
Cela ne compilera pas, mais cela fonctionnerait-il?
la source
Si vous utilisez des exemples pratiques pour vos types, vous le verrez:
Et maintenant vous avez votre fonction qui prend l' ancêtre ( ie
Object
):Qu'est-ce qui ne va pas avec ça?
Vous venez de réussir à attribuer un
Bitmap
à votre fichierSqlConnection
.Ce n'est pas bon.
Réessayez avec d'autres:
Vous avez bourré un
OracleConnection
sur-dessus de votreSqlConnection
.la source
Dans mon cas, ma fonction a accepté un objet et je ne pouvais rien envoyer alors j'ai simplement fait
Et ça marche
Mon Foo est dans VB.NET et il vérifie le type à l'intérieur et fait beaucoup de logique
Je m'excuse si ma réponse est en double mais les autres étaient trop longues
la source