2010-01-20 110 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>(它是隻讀的)。