2012-12-23 77 views
3

假設我有以下幾點:約束更嚴格的繼承方法?

class EntityContainer : Container { } 
class EntityComponent : Component { } 

Container都有添加新的組件容器,它們是兩種方法:

Add(IComponent component) 
Add(IComponent component, string name) 

然而,假設我希望我的EntityContainerEntityComponent對象,而不是實現IComponent的任何東西。

起初,我認爲我可以簡單地隱藏或覆蓋基類的Add()方法,但似乎簽名必須與完全匹配。那麼,做這件事的最好方法是什麼?

+0

是否有可能使基類的通用? – Ryan

+2

在你的'Add'方法中,如果傳遞的類型不兼容,則失敗:'if(!(component is EntityContainer))throw new InvalidArgumentException(「component」);'。一旦一個類通過,你可以將它轉換爲預期的類型:'var c = component as EntityContainer;'並且從那裏使用它,就像你直接接收它一樣。 –

+0

這些是默認的System.ComponentModel.Component和.Container的更具體的實現,所以我傾向於「不」。 –

回答

4

'覆蓋'Add方法,因此它接收更具體的類型將不會履行您的界面所暗示的合同。

你說的那個容器界面有以下方法:

void Add(IComponent component); 
void Add(IComponent component, string name); 

但你希望只允許EntityContainer相關實例(它們實現IComponent的),所以基本上你想要這個:

void Add(EntityComponent component); 
void Add(EntityComponent component, string name); 

你可以't實現(甚至語義上)這樣的Container接口,因爲在你的接口中,你說你可以添加任何實現IComponent的元素。你正在改變原來的合同!

由於莫滕在評論中指出,你可以做這樣的事情:

class EntityContainer : Container { 
    void Add(IComponent component) { 
     var entityComponent = component as EntityComponent; 
     if(entityComponent == null) 
      throw new InvalidOperationException("Can only add EntityComponent instances"); 
     // Actual add... 
    } 
    // Other methods.. 
} 

但我建議你不這樣做。打破界面所暗示的合同應該是例外,而不是規則。另外,如果你這樣做了,你就不知道Container在運行之前真正期望什麼。這不是一種直觀的行爲,它很可能會導致微妙的問題。如果您只想接受特定類型的組件,則可以使用泛型。通過這種方式,你不僅可以應用你想要的約束,你也將獲得強大的打字,並且你的意圖會更加清晰。它看起來像這些:

interface Container<T> where T : IComponent { 
    void Add(T component); 
    void Add(T component, string name); 
} 

這意味着,您的容器將持有指定類型的元素,但它應該實現(或延長,如果它是一個類)的接口IComponent的。所以你不能創建一個Container<Object>,因爲它沒有實現IComponent。

你EntityContainer相關應該是這樣的:

class EntityContainer : Container<EntityComponent> { 
    void Add(EntityComponent component) { 
     // Actual add... 
    } 
    // Other methods.. 
} 
+0

是的。 [LSP](http://en.wikipedia.org/wiki/Liskov_substitution_principle) –

3

協方差參數打破了類型系統。考慮這個:

void f(Container c) { c.Add(new NonEntityComponent); } 
⋮ 
var ec = new EntityContainer(); 
f(ec); 

類型系統中沒有任何東西可以阻止這種情況。 EntityContainer中的派生方法被聲明爲Add(EntityComponent ec)的事實不起作用,因爲f()從未聽說過EntityContainer

允許協變參數會產生破壞類型系統的情況,例如,EntityContainer.Add()通過NonEntityComponent並將其視爲EntityComponent。某些形式的變異可以一致地實現:協變返回類型(C++具有這些),協變輸出參數以及參數中的逆變參數。由於我不知情的原因,這些都沒有實施。其實,逆變參數會有點傻,國際海事組織,所以我會很驚訝地看到他們出現。