实现接口的抽象基类

假设我有一个简单的抽象基类

abstract class Item : IDisplayable { public int Id { get; set; } public string Name { get; set; } public abstract void Print(); } 

我有一个inheritance自那样的类

  class Chair: Item { public int NumberOfLegs {get;set;} public void Print() { Console.WriteLine("Here is a simple interface implementation"); } } interface IDisplayable { void Print(); } 

子类没有明确地说它也实现了接口,但它将通过简单的inheritance来实现。 如果我们明确地将接口添加到子类,程序将运行相同的程序(至少在我的简单示例中可以告诉我)。 明确地实现接口是一个好的或坏的想法,还是严格来说是一个偏好的问题?

如果我们明确地将接口添加到子类,程序将运行相同的程序(至少在我的简单示例中可以告诉我)。

该计划不一定会运行; 你的例子不足以说明差异。

明确地实现接口是一个好的或坏的想法,还是严格来说是一个偏好的问题?

除非您打算确保接口重新实现语义,否则这是一个坏主意。

让我简要说明一下。 这个程序做了什么?

 using System; interface IFoo { void Bar(); void Baz(); } class Alpha : IFoo { void IFoo.Bar() { Console.WriteLine("Alpha.Bar"); } void IFoo.Baz() { Console.WriteLine("Alpha.Baz"); } } class Bravo : Alpha { public void Baz() { Console.WriteLine("Bravo.Baz"); } } class CharlieOne : Bravo { public void Bar() { Console.WriteLine("CharlieOne.Bar"); } } class CharlieTwo : Bravo, IFoo { public void Bar() { Console.WriteLine("CharlieTwo.Bar"); } } class Program { static void Main() { IFoo foo = new Alpha(); foo.Bar(); foo.Baz(); foo = new Bravo(); foo.Bar(); foo.Baz(); foo = new CharlieOne(); foo.Bar(); foo.Baz(); foo = new CharlieTwo(); foo.Bar(); foo.Baz(); } } 

在您继续阅读之前,请认真: 尝试预测此程序的输出

现在实际运行它。 你得到了预期的输出吗? 你的直觉在哪里错了?

你现在看到CharlieOneCharlieTwo之间的区别吗? CharlieTwo重新实现IFoo可能导致接口绑定拾取Bravo.Baz即使Bravo 没有重新实现IFoo

而另一方面:如果您希望将Bravo.Baz分配给接口插槽只是因为它存在,那么您会看到如何重新实现接口失败会导致代码不正确。 对于Bravo.Baz来取代Alpha.IFoo.BazBravo必须重新实现IFoo

这里要说的是: 当你重新实现一个接口时, 所有的接口绑定都是从头开始重新计算的。 这可能会导致程序中的语义更改,因此只有在您打算这样做时才重新实现接口

这也说明了脆弱基类失败的另一种forms。 假设当你写Charlie时, Bravo没有方法Baz 。 如果你写Charlie重新实现IFoo那么Bravo的作者之后添加Baz – 也许是Bravo的作者在你公司的另一个团队 – 改变了Charlie的界面绑定,即使这不是Bravo的作者意。

有关更多信息,请参阅我关于此主题的文章:

http://blogs.msdn.com/b/ericlippert/archive/2011/12/08/so-many-interfaces-part-two.aspx

由于Iteminheritance自IDisplayable ,因此从Item派生的任何内容也必须实现IDisplayable 。 因此,明确地将IDisplayable添加到这些类是多余的。

编辑:@EricLippert在下面重点介绍了重新实现接口的效果。 我将离开这个答案以保留评论中的讨论。

当你说’显式实现接口’时,我认为你的意思是在类声明中包含接口。 明确实现接口有不同的具体含义。 话虽如此,声明派生类实现了基类实现的接口是不正确的,尽管编译器将允许它, 因为派生类不包含该接口的实现 。 考虑如果基类明确实现接口会发生什么 –

 abstract class Item : IDisplayable { void IDisplayable.Print() { ... } } 

现在派生类没有对基类方法的可见性,也没有派生类的使用者。 当然,消费者应该使用接口而不是具体的派生类,然后才能访问显式接口方法。 但是在派生类本身的范围内,它并不知道基类实现的接口。 只有一些成员是抽象的,受保护的或虚拟的。