2011-06-11 186 views
4
interface Base { ... } 
class Sub : Base { ... } 

class OtherBase<T> where T : Base { ... } 
class OtherSub<T> : OtherBase<T> where T : Base { ... } 

//...in some class 
void Call<T>() where T : OtherBase<Base> { } 

//... 
Call<OtherSub<Sub>>(); //compile fails... 

使用泛型時等看來,編譯器將不會在 一般類型投下內通用類型(基本/子)(OtherBase/OtherSub)。爲什麼會發生?泛型鑄造

更新: 還請解釋一下上面的之間的區別如下(工作)

void Call<T>() where T : Base { } 
//... 
Call<Sub>(); 

回答

8

險要這種行爲(稱爲「通用方」)是必要的,否則下面的代碼會編譯:

List<string> strlist = new List<string>(); 
List<object> objlist = strlist; 
objlist.Add(42); 

我們已經在字符串列表中添加了一個數字。不好。 (順便說一下,代碼編譯陣列而不是List是因爲Java的允許這種出於某種原因;但是,這將引發一個運行時異常。)

你可以在你的情況下避免這種情況,但:

static void Call<U, T>(T x) where U : Base where T : OtherBase<U> { } 

,並調用它是這樣的:

Call(new OtherSub<Sub()); 

C#4.0還提供generic variance for interfaces。但是,它們的使用往往不是必需的。

+0

這對於給出的示例有很大的意義,但仍然不適用於Call方法。你能否解釋一下你的例子和「void Call ()其中T:Base {}」,「請致電()」的區別? – jameszhao00 2011-06-11 15:28:22

+0

@james真的,我的錯誤。修正了。您的擴展問題與通用差異完全無關,因此沒有理由不適用。 – 2011-06-11 15:34:20

3

您的問題與稱爲方差/協方差的概念有關。實際上,如果A繼承自BClass<A>不是Class<B>

見下面的例子:

Class<T>暴露的公共方法foo(T param)

如果Class<A>Class<B>,然後不必Class<B>參考作爲Class<A>和調用foo(B param)(具有B實例)的方法,將是呼籲foo(A param)。而B不是A

實際上,僅當T僅用作Class<T>中的返回值時,Class<A>才能從Class<B>繼承。

這是在.NET 4中通過泛型接口的out關鍵字實施的。因此Class<T>可以實現IClass<out T>

1

Konrad對如何修復您的代碼有很好的建議。如果你想使用C#4的變化,你可以做這樣的:

interface IOtherBase<out T> where T : Base { } 

class OtherBase<T> : IOtherBase<T> where T : Base { } 
class OtherSub<T> : OtherBase<T> where T : Base { } 

static void Call<T>() where T : IOtherBase<Base> { } 

Call<OtherSub<Sub>>()會工作,然後。