2010-01-20 3 views
4

Je viens de me lancer dans ce que je pense être une bizarrerie dans le moulage de types. J'ai un code similaire à ce qui suit:C# Type impair de coulée de type - interface comme type générique

interface IMyClass { } 

class MyClass: IMyClass { } 

class Main 
{ 
    void DoSomething(ICollection<IMyClass> theParameter) { } 

    HashSet<MyClass> FillMyClassSet() 
    { 
    //Do stuff 
    } 

    void Main() 
    { 
    HashSet<MyClass> classSet = FillMyClassSet(); 
    DoSomething(classSet); 
    } 
} 

Quand il arrive à DoSomething (classSet), le compilateur se plaint qu'il ne peut pas jeter HashSet <MyClass> à ICollection <IMyClass>. Pourquoi donc? HashSet implémente ICollection, MyClass implémente IMyClass, alors pourquoi la distribution n'est-elle pas valide? Incidemment, ce n'est pas difficile à contourner, j'ai pensé que c'est un peu gênant.

void Main() 
{ 
    HashSet<MyClass> classSet = FillMyClassSet(); 
    HashSet<IMyClass> interfaceSet = new HashSet<IMyClass>(); 
    foreach(IMyClass item in classSet) 
    { 
    interfaceSet.Add(item); 
    } 
    DoSomething(interfaceSet); 
} 

Pour moi, le fait que cela fonctionne rend l'impossibilité de lancer encore plus mystérieux.

+0

@ Mehrdad a votre réponse quant à pourquoi. Mais au lieu de 'foreach', ne pouvez-vous pas simplement changer le type de retour de' FillMyClassSet() 'à' HashSet '? – JMD

Répondre

8

Cela ne fonctionnera pas car toutes les instances de MyClass étant IMyClass n'implique pas automatiquement que toutes les instances de HashSet<MyClass> sont également HashSet<IMyClass>. Si cela a fonctionné, vous pouvez:

ICollection<IMyClass> h = new HashSet<MyClass>(); 
h.Add(new OtherClassThatImplementsIMyClass()); // BOOM! 

Techniquement, cela ne fonctionne pas parce que C# (< = 3,0) génériques sont invariantes. C# 4.0 introduit une covariance de sécurité, ce qui n'aide pas non plus dans ce cas. Cependant, cela aide lorsque les interfaces utilisent les paramètres de type uniquement en entrée ou seulement dans les positions de sortie. Par exemple, vous serez en mesure de passer un HashSet<MyClass> en tant que IEnumerable<IMyClass> à une méthode.

Par ailleurs, ils sont des solutions de contournement plus facile que de remplir manuellement un autre HashSet comme:

var newSet = new HashSet<IMyClass>(originalSet); 

ou vous pouvez utiliser la méthode Cast si vous voulez lancer un ensemble à un IEnumerable<IMyClass>:

IEnumerable<IMyClass> sequence = set.Cast<IMyClass>(set); 
+2

+1 Bingo ..... 15 – JMD

+0

Si je m'étais donné plus de temps j'aurais dû m'en rendre compte finalement. L'information 4.0 est intéressante. Donc, si je peux le changer en DoSomething (IEnumerable theParameter), dans 4.0 le cast fonctionnerait? Autrement dit, en supposant que je devrais vraiment utiliser IEnumerable. – JamesH

+0

Oui; dans 4.0 qui fonctionnerait et ce sera sûr. Dans la version 3.0, vous devez utiliser la méthode 'Cast' (même si c'est sûr, le compilateur et l'exécution n'ont aucune idée à ce sujet). –

2

Un tel plâtre ne serait pas sûr.

Tenir compte de ce code:

class MyOtherClass: IMyClass { } 

void DoSomething(ICollection<IMyClass> theParameter) { theParameter.Add(new MyOtherClass(); } 

ICollection<MyClass> myClassSet = ...; 
DoSomething(classSet); //Oops - myClassSet now has a MyOtherClass 

Qu'est-ce que vous demandez que l'on appelle covariance; il est disponible pour IEnumerable<T> (qui est en lecture seule) dans C# 4.