假设我有一个简单的抽象基类,如
abstract class Item : IDisplayable
{
public int Id { get; set; }
public string Name { get; set; }
public abstract void Print();
}
我有一个继承自
的类 class Chair: Item
{
public int NumberOfLegs {get;set;}
public void Print()
{
Console.WriteLine("Here is a simple interface implementation");
}
}
interface IDisplayable
{
void Print();
}
子类没有明确地说它也实现了接口,但它将通过简单的继承来实现。如果我们将接口显式添加到子类,程序将运行相同的程序(至少在我的简单示例中可以告诉我)。明确地实现界面是一个好主意还是坏主意,还是严格来说是偏好?
答案 0 :(得分:43)
如果我们明确地将接口添加到子类,程序将运行相同的程序(至少在我的简单示例中可以告诉我)。
该计划不一定会运行;你的例子不足以说明不同之处。
明确地实现界面是一个好主意还是坏主意,还是严格来说是偏好?
除非您打算确保接口重新实现语义,否则这是一个坏主意。
让我简要说明一下。这个程序做了什么?
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();
}
}
在您继续阅读之前,请认真:尝试预测此计划的输出。
现在实际运行它。 您是否获得了预期的输出?你的直觉在哪里错了?
您现在看到CharlieOne
和CharlieTwo
之间的区别吗? 在IFoo
中重新实施CharlieTwo
会导致界面绑定选择Bravo.Baz
,即使Bravo
不重新实现{ {1}} <!/强>
另一方面:如果您希望将IFoo
分配给接口插槽只是因为它存在,那么您会看到失败如何重新实现接口导致代码不正确。要Bravo.Baz
替换Bravo.Baz
,Alpha.IFoo.Baz
必须重新实施Bravo
。
这里要说的是:当你重新实现一个接口时,所有接口绑定从头开始重新计算 。这会导致语义程序中的更改,因此仅在您打算执行时重新实现界面。
这也说明了另一种形式的脆弱基类失败。假设您IFoo
时Bravo
没有方法Baz
。如果您撰写Charlie
以重新实施Charlie
,则IFoo
的作者之后会添加Bravo
< - em> - 也许是{{1}的作者在您公司的不同团队中 - 更改Baz
内的界面绑定,即使这不是Bravo
的作者所期望的。
有关详情,请参阅我关于此主题的文章:
http://blogs.msdn.com/b/ericlippert/archive/2011/12/08/so-many-interfaces-part-two.aspx
答案 1 :(得分:4)
由于Item
继承自IDisplayable
,因此Item
派生的所有内容也必须实现IDisplayable
。因此,明确地将IDisplayable
添加到这些类是多余的。
答案 2 :(得分:0)
当你说'显式实现接口'时,我认为你的意思是在类声明中包含接口。显式实现接口有不同的具体含义。 话虽如此,声明派生类实现了基类实现的接口是不正确的,尽管编译器将允许它,因为派生类不包含该接口的实现。 考虑如果基类明确实现接口会发生什么 -
abstract class Item : IDisplayable
{
void IDisplayable.Print() { ... }
}
现在派生类没有对基类方法的可见性,也没有派生类的使用者。当然,消费者应该使用接口而不是具体的派生类,然后才能访问显式接口方法。但是在派生类本身的范围内,它并不知道基类实现的接口。只有一些成员是抽象的,受保护的或虚拟的。