2016-07-25 1 views
1

C# 6.0 en bref par Joseph Albahari et Ben Albahari (O'Reilly).Type Erreurs d'exécution de covariance

Copyright 2016 Joseph Albahari et Ben Albahari, 978-1-491-92706-9.

états, aux pages 123-124, en ce qui concerne de type covariance:

tableaux, pour des raisons historiques, prennent en charge les types de tableaux covariance. Ce signifie que B [] peut être converti en A [] si B sous-classe A (et les deux sont types de référence).

Par exemple:

Bear[] bears = new Bear[3]; 
Animal[] animals = bears; // OK 

L'inconvénient de cette réutilisabilité est que les affectations des éléments peuvent échouer lors de l'exécution:

animals[0] = new Camel(); // Runtime error 

Quelle est la raison derrière une telle erreur? Si vous affectez une instance de Bear à une instance d'Animal, une erreur d'exécution sera générée? Je ne vois pas pourquoi il devrait le faire (en autorisant une telle tâche, le compilateur doit prendre la responsabilité de dire «D'accord, je vais vous laisser faire avec cet objet tout ce qu'un animal peut faire.» Puisque Bear est un animal, . aucun problème

J'ai créé mon propre scénario pour tester ce qui précède:

public class X 
{ 
    public int Num { get; set; } 

    public void Method_1() 
    { 
     Console.WriteLine("X"); 
    } 

    public virtual void Method_2() 
    { 
     Console.WriteLine(Num); 
    } 
} 

public class Y : X 
{ 
    public Y() 
    { 
     Num = 1000; 
    } 
} 

X[] arrayX = new X[] { new X { Num = 1000 }, new X { Num = 999 }, new X { Num = 51762 } }; 
Y[] arrayY = new Y[] { new Y { Num = 5 }, new Y { Num = 6 }, new Y { Num = 7 } }; 

X x = new X { Num = 1000 }; 
Y y = new Y { Num = 50 }; 

x = y; 

arrayX = arrayY; 

arrayX[2] = new Y { Num = 1 }; 

// will print 5,6,1 - no runtime errors faced 
foreach (var e in arrayX) 
    Console.WriteLine(e.Num); 

Je crois que l'extrait ci-dessus imite l'exemple du livre - mais avec mon extrait, il

aucune erreur d'exécution sont. Comment est-ce que animals[0] = new Camel(); est censé avoir généré une erreur d'exécution, comme l'indique le livre?

+1

Alors que le tableau 'animals' ressemble à un tableau d'animaux, c'est en fait un tableau d'ours. Vous ne pouvez pas ajouter un chameau à une rangée d'ours (clairement les ours mangeraient le chameau). Votre exemple a l'animal (X) et l'ours (Y), mais il manque une deuxième classe d'enfant comme le chameau. – juharr

Répondre

4

Quelle est la raison de cette erreur?

Parce qu'il tente de stocker un Camel dans un tableau avec un type de Bear[] d'exécution. Un tableau de type Bear[] peut seulement stocker les références aux instances de Bear ou sous-classes. Le type de compilation de Animal[] dit seulement qu'il pourrait pouvoir stocker une référence Camel, et que toute référence vous sur du tableau sera certainement une instance Animal ou sous-classe.

Votre exemple est différent. Lorsque nous supprimons toutes les propriétés etc (qui ne sont pas pertinentes) que vous avez:

X[] arrayX = new Y[3]; 
arrayX[2] = new Y(); 

C'est très bien - c'est le stockage d'une référence à un objet Y dans un tableau avec un type d'exécution en temps Y[]. Aucun problème.

Pour démontrer le même problème que le livre, vous auriez besoin d'une troisième classe:

class Z : X {} 

X[] arrayX = new Z[3]; 
arrayX[2] = new Y(); // Bang - can't store a Y reference in a Z[] 
+0

@Veverke: Je ne suis pas sûr de ce que vous voulez dire - mais je regarde maintenant votre exemple plus impliqué. –

+0

Bonjour Jon, ma question concerne la déclaration du livre 'animals [0] = new Camel(); // Erreur d'exécution ». – Veverke

+3

@Veverke C'est ce que Jon a répondu. – Servy

1

Votre exemple avec X et Y est tout à fait différent de celui du livre. Si vous souhaitez imiter celui du livre, créez une classe de base abstraite X et faites en dériver Y et Z. Ensuite, jouez avec. L'un de livre implique que:

class Program 
{ 
    static void Main(string[] args) 
    { 
     Bear[] bears = new Bear[3]; 
     Animal[] animals = bears; 
     animals[0] = new Camel(); //will throw on runtime 
    } 
} 

public abstract class Animal { } 

public class Camel : Animal { } 

public class Bear : Animal { } 

Comme Jon déjà dit, le type d'exécution de animals sera Bear[] qui peut tout simplement pas stocker une instance de Camel. s'il vous plaît Notez également, c'est une erreur possible pour la conversion du tableau covariant, mais ne va pas se produire pour d'autres collections comme List:

 List<Bear> bearsList = new List<Bear>(); 
     List<Animal> animalsList = bearsList; //won't compile because of this error 
     animalsList[0] = new Camel(); 
1

Dans la ligne suivante:

Animal[] animals = bears; 

Vous êtes juste cacher votre Bear[] en Animal[].

Notez que je dis cachant, car vous ne créez pas réellement un nouveau tableau.

Les deux bears et animals pointent vers le même Bear[], la seule différence est que animals se cache cette référence comme Animal[].


Le compilateur ne le sait pas.

Pour le compilateur, si vous voulez stocker un Dolphin ou un Lion sur animals, il vous permettra de le faire, étant donné que tous ces éléments sont de type Animal.


L'exécution serait cependant se plaindre.

Depuis animals cache Bear[], en ajoutant un Camel à ce ne sera pas autorisé.

Bien que Bear et Camel héritent tous les deux de Animal, ils sont deux types différents.

0

Pour ce code:

Animal[] bears = new Bear[3]; 

Si vous avez installé ReSharper dans votre VS, il donne le même avertissement:

conversion de tableau Co-variante de Bear [] pour animaux [] peut provoque l'exécution exception lors de l'opération d'écriture.

Si vous utilisez générique comme IList:

IList<Animal> bears = new List<Bear>(); 
bears.Add(new Camel()); 

la première ligne ne vous laissera pas compilez, ce qui est plus sûr, il empêche d'exception d'exécution possible.