59 votes

Pourquoi le compilateur C # permet-il une conversion explicite entre IEnumerable <T> et presque rien?

Le code suivant vous donne une erreur de compilation, comme vous le souhaitiez:

 List<Banana> aBunchOfBananas = new List<Banana>();

Banana justOneBanana = (Banana)aBunchOfBananas;
 

Cependant, lorsque vous utilisez IEnumerable<Banana> , vous obtenez simplement une erreur d'exécution.

 IEnumerable<Banana> aBunchOfBananas = new List<Banana>();

Banana justOneBanana = (Banana)aBunchOfBananas;
 

Pourquoi le compilateur C # permet-il cela?

48voto

Yuck Points 23174

Je suppose que c'est parce qu' IEnumerable<T> est une interface où certains de la mise en œuvre pourrait avoir un cast explicite Banana - peu importe comment stupide que serait.

D'autre part, le compilateur sait qu' List<T> ne peut pas être explicitement exprimées à une Banana.

Bon choix des exemples, par la manière dont!

Ajout d'un exemple pour clarifier. Peut-être que nous aurions des "énumérable" qui doit toujours contenir au plus un seul Banana:

public class SingleItemList<T>:Banana, IEnumerable<T> where T:Banana {
    public static explicit operator T(SingleItemList<T> enumerable) {
        return enumerable.SingleOrDefault();
    }

    // Others omitted...
}

Ensuite, vous pourriez faire ça:

IEnumerable<Banana> aBunchOfBananas = new SingleItemList<Banana>();
Banana justOneBanana = (Banana)aBunchOfBananas;

Comme c'est la même que l'écriture de la suite, dont le compilateur est parfaitement heureux avec:

Banana justOneBanana = aBunchOfBananas.SingleOrDefault();

29voto

Jason Points 125291

Quand vous dites Y y = (Y)x; cette distribution indique au compilateur "faites-moi confiance, quel que soit x est, à l'exécution, il peut être utilisé pour un Y, donc, il suffit de le faire, d'accord?"

Mais quand vous dites

List<Banana> aBunchOfBananas = new List<Banana>();
Banana justOneBanana = (Banana)aBunchOfBananas;

le compilateur peut regarder les définitions de chacune de ces classes concrètes (Banana et List<Banana>) et de voir qu'il n'y a pas d' static explicit operator Banana(List<Banana> bananas) défini (rappelez-vous, un cast explicite doit être défini dans le type coulée ou le type en train de coulé, c'est à partir de la spécification, la section 17.9.4). Il sait au moment de la compilation que ce que vous dites ne peut jamais être vrai. Donc, il hurle à vous d'arrêter de mentir.

Mais quand vous dites

IEnumerable<Banana> aBunchOfBananas = new List<Banana>();
Banana justOneBanana = (Banana)aBunchOfBananas;

bien, maintenant que le compilateur ne sait pas. Il pourrait très bien le cas que, quelle que soit aBunchOfBananas arrive à être au moment de l'exécution, son type de béton X pourrait avoir défini static explicit operator Banana(X bananas). Ainsi, le compilateur a confiance en vous, comme vous l'avez demandé.

16voto

eouw0o83hf Points 3822

C’est peut-être parce que le compilateur sait que Banana n’étend pas List<T> , mais il est possible que certains objets qui implémentent IEnumerable<T> puissent également étendre Banana et en faire un casting valide.

0voto

Alex Points 63

Selon le langage de spec (6.2.4) "La référence explicite les conversions sont: De toute la classe-type S à n'importe quelle interface de type T, à condition que S n'est pas scellé et à condition S ne pas mettre en œuvre des T ... La référence explicite les conversions sont les conversions entre de référence des types d'exiger l'exécution des vérifications pour s'assurer qu'ils sont corrects..."

Donc le compilateur ne vérifie pas l'interface de réalisation lors de la compilation. Il ne CLR dans l'exécution. Il vérifie les métadonnées d'essayer de trouver de réalisation en classe ou chez ses parents. Je ne sais pas pourquoi il se comporte comme cela. Probablement, il prend beaucoup de temps. Donc, ce code compile correctement:

public interface IInterface
{}

public class Banana
{
}

class Program
{
    static void Main( string[] args )
    {
        Banana banana = new Banana();

        IInterface b = (IInterface)banana;
    }
}

En revanche, si on essaie de jeter de la banane pour la classe, le compilateur vérifie ses métadonnées et renvoie une erreur:

 FileStream fs = (FileStream)banana;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X