2011-07-19 81 views
14
public abstract class EntityBase { ... } 

public interface IFoobar 
{ 
    void Foo<T>(int x) 
     where T : EntityBase, new(); 
} 

public interface IFoobar<T> 
    where T : EntityBase, new() 
{ 
    void Foo(int x); 
} 

public class Foobar<T> : IFoobar, IFoobar<T> 
    where T : EntityBase, new() 
{ 
    public void Foo(int x) { ... } 

    void IFoobar.Foo<T>(int x) { Foo(x); } 
} 

我得到一個編譯器警告:Type parameter 'T' has the same name as the type parameter from outer type '...'類型參數「T」具有相同的名稱從外類型的類型參數「...」

我想這樣做:void IFoobar.Foo<U>(int x) { Foo(x); },但我就不能保證U和T是一樣的。 Foobar課程的實施方式非常重要,它們是相同的。

我也試過:void IFoobar.Foo<U>(int x) where U : T { Foo(x); },但不能保證U和T是相等的,它不允許我重新定義約束,因爲它是在接口上定義的。

+0

你想要做什麼?如果在課堂上指定了T,爲什麼要重新定義T?如果你需要定義它,那麼它應該是分開的,否則T已經被類指定了。如果你想解決這個問題,如果可以的話,把T放在界面上。當我發現自己複製了類型規格時,這就是我所做的。 –

回答

8

你可以做到這一點與它去(如果你正確地任何歷史的對象上操作),在錯誤的減少兩種情況之一:

  1. 忽略警告,並讓這兩種類型T.
  2. 做一個運行時檢查,並拋出一個異常:

    if (typeof(T) != typeof(U)) throw Exception("Not the same type"); 
    

正如其他人所指出的,也許你需要重新考慮你的方式設計你的界面。

3

試試看

void IFoobar.Foo<U>(int x) { Foo(x); } 

當然,這仍然不能保證U相同T。你不能在編譯時強制執行,因爲當你實現一個接口時,你必須遵循其規則 - 而IFoobar不會對Foo<T>設置這樣的限制,如果你這樣做,你會不再是實現界面(根據定義,因爲你更嚴格,但你聲稱你不是)。

你可以嘗試在運行時檢查它,雖然這有點「作弊」(因爲你不符合接口,然後)。

+0

雖然這保持了對類型的約束,但並不保證'T'和'U'是相同的。 –

+0

這並不能保證U等於T,並且我不能重新定義U上的約束,因爲它們是在界面中定義的。 – michael

+0

這不會編譯。 '覆蓋和顯式接口實現方法的約束是從基本方法繼承的,所以它們不能直接指定。刪除where子句來解決這個問題。 –

12

最大的問題是你的接口沒有很好的定義,並且不符合你的代碼的意圖。

如果您的T在界面上沒有公開顯示,那麼外部代碼甚至不必知道存在T。您需要制定接收或返回T的方法,或者擁有T類型的某些屬性,或者您應該完全擺脫T,並使接口非通用。

一旦你掌握了這一點,它應該變得更加明顯,爲什麼你在這裏不需要兩個不同的接口,你不應該再調和它們。

如果事實證明你需要一個版本需要T和非-T版本,那麼更地道的方式來做到這一點的是通過圍繞object代替T

public interface IFoo 
{ 
    void DoSomething(object o); 
    object DoSomethingElse(); 
} 

public interface IFoo<T> 
{ 
    void DoSomething(T item); 
    T DoSomethingElse(); 
} 

有關這方面的示例,請參閱IEnumerableICollectionIList等接口。

但仔細考慮。最後的設計折中(同時具有通用版本和對象版本)總會留下一些不足之處。

你會犧牲其中之一:

  • 良好的界面設計,直接通信的設計合同(如果你拋出異常或錯時類型傳遞在做任何操作)
  • 類型安全和
相關問題