2011-08-01 60 views
9

我有兩個商務合同類:行動代表,泛型,協方差和逆變

public BusinessContract 

public Person : BusinessContract 

在另一大類,我有以下代碼:

private Action<BusinessContract> _foo; 

public void Foo<T>(Action<T> bar) where T : BusinessContract 
{ 
    _foo = bar; 
} 

上面甚至不會編譯,這讓我有些困惑。我將T約束爲BusinessContract,爲什麼編譯器不知道bar可以分配給_foo?

在試圖解決這個問題,我們試圖將其更改爲以下:

public void Foo<T>(Action<T> bar) where T : BusinessContract 
{ 
    _foo = (Action<BusinessContract>)bar; 
} 

現在,編譯器是幸福的,所以我在其他地方寫下面的代碼在我的應用程序:

Foo<Person>(p => p.Name = "Joe"); 

運行時應用程序爆發InvalidCastException。

我不明白。我應該不能將更具體的類型轉換爲不太特定的類型並分配它?

UPDATE

喬恩回答了這個問題,從而得到了該點頭,只是收在這個循環中,這裏是我們如何最終解決問題。

private Action<BusinessContract> _foo; 

public void Foo<T>(Action<T> bar) where T : BusinessContract 
{ 
    _foo = contract => bar((T)contract); 
} 

我們爲什麼要這樣做?我們有一個假的DAL,我們用於單元測試。使用其中一種方法,我們需要給測試開發人員指定在測試過程中調用方法時應該執行的操作(它是從數據庫更新緩存對象的刷新方法)。 Foo的目的是設置在刷新被調用時應該發生的事情。 IOW,在這個課程的其他地方,我們有以下內容。

public void Refresh(BusinessContract contract) 
{ 
    if(_foo != null) 
    { 
     _foo(contract); 
    } 
} 

例如,測試開發人員可以決定當調用Refresh時他們想要將名稱設置爲不同的值。

Foo<Person>(p => p.Name = "New Name"); 

回答

13

你有錯誤的方式協變和逆變。我們來考慮Action<object>Action<string>。卸下實際仿製藥,你試圖做這樣的事情:

private Action<object> _foo; 

public void Foo(Action<string> bar) 
{ 
    // This won't compile... 
    _foo = bar; 
} 

現在假設我們接着寫:

_foo(new Button()); 

這很好,因爲Action<object>可以傳遞任何對象...但我們已經用代理初始化它,其中必須接受一個字符串參數。哎喲。

這不是類型安全的,所以不能編譯。

另一種方式工作雖然:

private Action<string> _foo; 

public void Foo(Action<object> bar) 
{ 
    // This is fine... 
    _foo = bar; 
} 

現在,當我們調用_foo,我們一個字符串傳遞 - 但是這很好,因爲我們有一個委託初始化它這可以採用任何object引用作爲參數,所以我們碰巧給它一個字符串很好。

所以基本上Action<T>逆變 - 而Func<T>

Func<string> bar = ...; 
Func<object> foo = bar; // This is fine 
object x = foo(); // This is guaranteed to be okay 

目前尚不清楚你想要什麼與動作,所以很遺憾我真的不能給任何關於如何解決這個問題的建議......

+0

@MDeSchaepmeester:不,我不是 - 在工作代碼中(後半部分),我將一個'Action '值('bar')賦值給一個'Action '變量('_foo')然後爲'Func '變量('foo')分配一個'Func '值('bar)'。 –

+0

你是對的,愚蠢的錯誤,我沒有真正讀過這段代碼,並假定你用_foo作爲參數調用Foo(我來這裏是因爲我在做* *) – MarioDS

0

因爲既然你使用的是逆變,而不是協變的,不能被分配,沒有辦法保證泛型類型可以被分配到foo中。

+0

那麼,這不是真的「因爲[OP]使用泛型」 - 是因爲'Action '是逆變而不是cov ariant。 –

+0

@Jon Skeet,你是​​對的,我沒有解釋得很好,我會編輯它。 –