2010-01-20 76 views
4

我刚刚遇到了我认为是类型转换中的一个怪事。我有类似下面的代码:C#类型转换奇数 - 接口作为通用类型

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); 
    } 
} 

当它到达的DoSomething(classSet),编译器抱怨说,它不能施放HashSet的< MyClass的>到ICollection的<IMyClass>。这是为什么? HashSet实现ICollection,MyClass实现IMyClass,那么为什么不是有效的?

顺便说一句,这不是很难解决,认为它有点尴尬。

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

对我来说,这个作品使得无法投射更加神秘。

+0

@ Mehrdad的答案是为什么。但是,而不是'foreach',你能不能把'FillMyClassSet()'的返回类型改为'HashSet '? – JMD 2010-01-20 20:49:46

回答

8

它不起作用,因为MyClass的所有实例都是IMyClass并不自动暗示HashSet<MyClass>的所有实例也都是HashSet<IMyClass>。如果它的工作,你可以:

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

从技术上说,这是行不通的,因为C#(< = 3.0)泛型是不变的。 C#4.0引入了安全协变,在这种情况下也没有帮助。但是,当接口仅在输入或仅在输出位置使用类型参数时,它确实有帮助。例如,您将能够通过HashSet<MyClass>作为IEnumerable<IMyClass>某种方法。

顺便说一句,他们更容易的解决方法比手动灌装另一HashSet,如:

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

,或者如果你想一套强制转换为IEnumerable<IMyClass>可以使用Cast方法:

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

+1 Bingo ..... 15 – JMD 2010-01-20 20:48:32

+0

如果我给自己更多的时间,我应该最终意识到这一点。 4.0信息很有趣。因此,如果我可以将它改为DoSomething(IEnumerable theParameter),那么在4.0中投射会起作用?也就是说,假设我真的应该使用IEnumerable。 – JamesH 2010-01-20 20:55:48

+0

是的;在4.0将工作,它会很安全。在3.0中,你必须使用'Cast'方法(即使它是安全的,编译器和运行时也不知道它)。 – 2010-01-20 20:57:23

2

这样的演员阵容实际上并不安全。

考虑以下代码:

class MyOtherClass: IMyClass { } 

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

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

什么你问的是所谓的协方差;它适用于C#4中的IEnumerable<T>(它是只读的)。