2017-07-20 45 views
0

在下面的示例類「SomeClass」中沒有實現「ISomeInterface」。爲什麼我不能通過傳遞實現基本要求的更多派生接口來實現此目的。無論哪個實例會通過它仍然會實現基地,我錯過了什麼?帶派生接口的C#接口實現

namespace Test 
{ 
    public interface IBaseInterface 
    { 
     void DoBaseStuff(); 
    } 

    public interface IChildInterface : IBaseInterface 
    { 
     void DoChildStuff(); 
    } 

    public interface ISomeInterface 
    { 
     void DoSomething(IBaseInterface baseInterface); 
    } 

    public class SomeClass : ISomeInterface 
    { 
     public void DoSomething(IChildInterface baseInterface) 
     { 
     } 
    } 
} 

回答

2

這違反了Liskov substitution principle

ISomeInterface保證可以用任何IBaseInterface實例調用該方法。您的實施不能將其限制爲只接受IChildInterface接口。

+1

雖然這是更通用的方式,但我認爲這裏的實際原因是C#編譯器只需要1對1的實現。如果情況逆轉(即接口需要子接口,實現允許基接口),那麼它不會違反Liskov,但這也是不合法的。 –

1

MSDN

當一個類或結構實現的接口,所述類或結構必須提供,所述接口定義

此方法中成員的所有一個實現派生的

void DoSomething(IChildInterface baseInterface) 

沒有相同的簽名作爲一個在接口:

void DoSomething(IBaseInterface baseInterface) 

IChildInterfaceIBaseInterface是不一樣的類型。因此,您的派生類不會實現接口的所有方法,並且會得到編譯錯誤。

對於可能有此作爲的限制,而不是編譯理解繼承看到Liskov的的替換原則作爲SLakes回答

1

存在這種限制,因爲ISomeInterface預計任何IBaseInterface將滿足合同背後的邏輯。也就是說,如果你具備以下條件:

public interface IBase {} 
public interface IChildA : IBase {} 
public interface IChildB : IBase {} 

這預計IBase接口:

public interface IFoo { void Bar(IBase val); } 

然後在派生類中限制這個,你想:

public class Foo : IFoo { public void Bar(IChildA val) {} } 

會產生以下問題:

IChildB something = new ChildB(); 
IFoo something = new Foo(); 
something.Bar(something); // This is an invalid call 

因此,你沒有執行你所說的合同。

在這種情況下,你有兩個簡單選項:

  • 調整IFoo是通用的,並接受T這是IBase推導:

    public interface IFoo<T> where T : IBase { void Bar(T val); } 
    public class Foo : IFoo<IChildA> { public void Bar(IChildA val) {} } 
    

    當然,這意味着Foo不能再接受任何IBase(包括IChildB)。

  • 調整Foo實施IFoo,另外還有實用方法void Bar(IChildA val)

    public class Foo : IFoo 
    { 
        public void Bar(IBase val) {} 
        public void Bar(IChildA val) {} 
    } 
    

    這有一個有趣的副作用:當你打電話((IFoo)foo).Bar會想到IBase,當你調用foo.Bar它會期望IChildAIBase。這意味着它滿足合同,同時也具有派生接口特定的方法。如果你想「隱藏」 Bar(IBase)方法更多,你可以實現IFoo明確:

    void IFoo.Bar(IBase val) { } 
    

    這會在你的代碼不一致的行爲,如現在((IFoo)foo).Bar完全foo.Bar不同,但我把決定留給你。

    這意味着,與本節中的第二個版本,即foo.Bar(new ChildB());現在是無效的,因爲IChildB一個IChildA


爲什麼我無法通過傳遞更多的衍生接口,確實實現了基地的要求實現這一點。無論哪個實例會通過它仍然會實現基地,我錯過了什麼?

這是不允許的,因爲我上面提到的推理,IFoo.Bar預計任何IBase,而要進一步約束類型IChildA,這是IBase超接口,即使它不允許,因爲它違反了接口實現,儘管你可以更容易地定義第二種方法,在那個時候你可以做到你想要的。

請記住,當你實現一個interface,你訂閱合同一個,和C#將讓你違反了該合同。

0

如果你能做到這一點,那麼你可以這樣做:

IAnimal cat = new Cat(); 
IAnimalTrainer dogTrainer = new DogTrainer(); 
dogTrainer.Train(cat); 

IAnimalTrainer可以訓練任何IAnimal。但一個DogTrainer只能訓練Dog s。因此,DogTrainer實施IAnimalTrainer接口是非法的。

0

你應該改變一些接口來使用一些實現IBaseInterface的類型,然後改變方法簽名來使用你的SomeClass想要的任何一個子類。

public interface ISomeInterface<TSomeChild> where TSomeChild : IBaseInterface 
{ 
    void DoSomething(TSomeChild baseInterface); 
} 

public class SomeClass : ISomeInterface<IChildInterface> 
{ 
    public void DoSomething(IChildInterface baseInterface) 
    { 
    } 
}