7
votes

Cast IList à IList échoue au moment de l'exécution

J'ai le programme C # bref suivant: xxx

Ce programme ne compile pas. La dernière ligne provoque l'erreur de compilation suivante:

ne peut pas convertir implicitement le type 'System.Collections.Generic.List' à 'System.collections.Generic.List'. Une conversion explicite existe (manquez-vous un casting?)

Donc, j'ai ajouté la distribution: xxx

Le programme compile correctement, mais échoue au moment de l'exécution. Un InvalidCastException est soulevé avec le message suivant:

Impossible de lancer Objet de type 'System.Collections.Generic.list'1 [System.string]' Pour taper 'System.Collections.Generic.List'1 [System.Object]'.

Soit le casting est illégal et doit être pris par le compilateur, soit il est légal et ne devrait pas jeter une exception au moment de l'exécution. Pourquoi le comportement incohérent?

Clarification: Je ne demande pas pourquoi le moule échoue. Je comprends pourquoi ce casting est problématique. Je demande pourquoi le moule échoue uniquement à l'exécution .


12 commentaires

Ceci est un problème de covariance par le livre. S'il vous plaît voir Stackoverflow.com/questions/5832094/covariant-and-ilist


Dupliqué possible de Covariant en C #


Et de commenter votre raisonnement. Qu'essayez-vous est logiquement faux. C'est pourquoi le compilateur n'aime pas. Essayer de le forcer en utilisant une distribution explicite ne va pas vous aider. L'utilisation d'une fonte explicite est comme dire "Je me fiche de quel type ceci est, il suffit de le compiler".


Je pose des questions sur l'incohérence dans le comportement - pourquoi la moulage explicite est-elle autorisée si elle échoue?


Parce que les moules ne sont vérifiées que lors de l'exécution. Ils ne sont jamais vérifiés lors de la compilation, car une distribution, par définition, utilise des informations que le compilateur ne sait pas (sinon une conversion implicite serait autorisée).


@Checoop, ce n'est tout simplement pas vrai. Essayez (double) DateTime.now , le compilateur ne le permettra pas.


@Euphoric, pourquoi le casting explicite existe-t-il? Cela ne devrait pas être là, comme cela échouera toujours. Dans quelles circonstances réussira-t-elle?


Il réussira lorsque le type interne de l'objet est assigné au type converti. Quelque chose comme "objet o = nouveau a (); a a = (a) o" va réussir. Dans votre cas, ilist n'est pas assignable à IList .


@Euphorique, quand est une IList assignable à un ?


Jamais. C'est pourquoi il ne compilera pas. La coulée explicite est pour les cas où vous êtes sûr que le type est assignable. Vous utilisez "is" opérateur pour le vérifier. En outre, vérifiez «comme» opérateur. Il retournera NULL au lieu d'échouer.


@ZMBQ: Oups. Ouais. C'est plus compliqué que "jamais". Mais ce qui compilera, c'est (double) (objet) datetime.now


Si le compilateur peut déterminer avec certitude, la distribution ne pourrait jamais réussir, elle n'est pas autorisée. Par exemple, un struct ou un Classe scellée ne peut pas être dérivé. Le compilateur sait donc exactement quelles interfaces il met en œuvre et connaît toutes ses classes de base. Tellement essayer de jeter une classe ou une classe scellée à quelque chose de sans rapport n'est pas autorisé à compiler. D'autre part, une classe qui n'est pas scellée peut être explicitement moulée à aucune interface car une personne aurait pu hériter de la classe et que la classe dérivée implémente l'interface "non liée". Et ainsi de suite ...


5 Réponses :


1
votes

Vous pouvez le faire par initialisateur de collecte. Les extraits de code ci-dessous fonctionnent pour moi. XXX


0 commentaires

2
votes

listObject.Add(10); // ok
string s = listString[0]; // WTF?!!!

Due to IList mutability such conversion is meaningless.

2 commentaires

Ce qui serait une excellente raison pour que le compilateur empêche la distribution. Cependant, la distribution est autorisée et échoue au moment de l'exécution. Pourquoi l'incohérence ici?


Il n'y a pas d'incohérence, une classe implémentant IList peut également mettre en œuvre IList et cette distribution (heure d'exécution) fonctionnera pour des objets de tels types. Les incorrects du cas que j'ai montré sont évidents à l'humain seulement, le compilateur ne peut pas faire une telle analyse et il pourrait y avoir beaucoup de cas lorsque les interfaces peuvent et doivent être convertibles vers d'autres interfaces. Il n'y a donc aucune raison d'interdire de telles coulées pour tous les cas et il n'ya aucun moyen de distinguer des moulages valides et non valides au moment de la compilation.



4
votes

Une façon de trouver la liste des chaînes à une liste d'objets serait la suivante:

IList<object> objects = listOfStrings.Cast<Object>().ToList();


3 commentaires

Notez également que l'ajout d'une chaîne à la liste résultante n'ajoute pas cette chaîne à la liste d'origine.


Merci, mais je ne demande pas comment accomplir réellement le casting. Je veux savoir pourquoi le compilateur permet la coulée même si elle est condamnée à échouer au moment de l'exécution.


@ZMBQ, car le compilateur ne fait pas ce genre d'analyse statique.



1
votes

Si vous ne pouvez pas accéder à la liste sous-jacente , xxx

si vous pouvez accéder à la liste sous-jacente , xxx


0 commentaires

11
votes

La raison pour laquelle la mise en forme implicite de iList à ilist ne compilera pas, comme vous semblez savoir, que le L'interface IList est pas covariant dans t . Si, avec .NET 4.5, vous avez utilisé iReadonlist au lieu de cela, cela fonctionnerait.

La raison pour laquelle la fonte explicite xxx

sera compilé, n'est pas que iList et ilist sont liés de quelque manière que ce soit. Aucun type n'est assigné à l'autre. La raison en est que le type d'exécution de votre variable ( liststring ) peut être une classe (ou une structure) qui implémentait les deux interfaces! Supposons que j'ai faite cette classe: xxx

alors si un ilist est arrivé à être un Crazyliste au moment de l'exécution, La fonte explicite serait réussir. Lorsque vous écrivez une distribution explicite, vous dites au compilateur "Je sais que le type va être convertible à ce type que je jette dans". Puisque le compilateur ne peut pas prouver que vous avez tort, bien sûr, il vous croit.


2 commentaires

@zmbq et vous pouvez faire une apparition explicite de toute interface à une autre interface autre interface pour la même raison, par exemple ICLONEABLEABLE MAILLEABLE = XXXX; Idisposable jetable = (isisposable) claquant; .


@ZMBQ, cela est également vrai pour les non génériques. L'idée de covariance est un hareng rouge ici. L'analyse statique nécessaire pour détecter l'erreur de développeur est coûteuse et généralement inutile.