C#接口实现关系只是“可以做”的关系?

时间:2008-11-01 08:18:35

标签: c# inheritance interface liskov-substitution-principle

今天有人告诉我,C#中的接口实现只是“Can-Do”关系,而不是“Is-A”关系。这与我长期相信LSP(Liskov Substitution Principle)相冲突。我一直认为所有的继承都应该意味着“Is-A”的关系。

所以,如果接口实现只是一个“可以做”的关系。如果有一个界面“IHuman”和“IEngineer”,并且一个类“Programmer”继承自“IHuman”& “IEngineer”?当然,“程序员”是“IHuman”和“IEngineer”。

如果它只是“Can-Do”关系,是否意味着我们不能指望“程序员”实例行为在被视为IHuman并被视为IEngineer时可能会有所不同?

5 个答案:

答案 0 :(得分:15)

根据我的经验,想到“is-a”和“can-do”关系并没有多大帮助。你很快就会遇到问题。它基本上是现实世界和OO之间的阻抗不匹配。然而,实际上很多人都在谈论对现实世界进行建模,您从根本上需要了解类型之间的关系在您正在使用的平台上的意义。

有时接口可以用作功能,有时它们可​​以代表更多正常的“is-a”关系。我不会太在意它 - 只要确保你明白他们能做什么,不能做什么。

答案 1 :(得分:5)

我倾向于将接口视为行为契约。诸如IComparable和IEnumerable之类的接口是经典示例。

在你给出的例子中,IHuman和IEngineer并不是真正的行为。

答案 2 :(得分:4)

发现这个问题的时间相当晚,但我想加入。

C#中的接口具有is-a关系,但不是is-an-object。相反,是实施。

换句话说,对于实现IBar的类Foo,进行以下测试:

Foo myFoo = new Foo(); 
return myFoo is IBar;

字面上返回true。你也可以说,

IBar bar = myArrayList[3] as IBar;
Foo foo = bar as Foo;

或者,如果方法需要IBar,则可以传递Foo。

void DoSomething(IBar bar) {
}

static void Main() {
    Foo myFoo = new Foo();
    DoSomething(myFoo);
}

显然,IBar本身没有实现,因此can-do关系也适用。

interface IBar { void happy(); }
class Foo : IBar
{
    void happy()
    {
        Console.Write("OH MAN I AM SO HAPPY!");
    }
}
class Program
{
    static void Main()
    {
        IBar myBar = new Foo();
        myBar.happy();
    }
}

但是,就此而言,对象继承也是如此;继承类Bar的类Foo的对象具有can-do关系以及is-a关系,与接口的方式相同。只是它的实现是为它预先构建的。

真正的问题是,is-a- 什么,可以做什么 ?继承的类对象是-a- [父实例] 和can-do- [parent的行为] ,而接口的实现是-an- [接口实现] 因此可以 - [接口行为]

在程序化的大多数情况下 - 使用上面列出的关系,只评估接口,因此继承类和实现的接口共享相同的is-a和can-do质量。

HTH, 乔恩

答案 3 :(得分:2)

.NET框架的设计者使用接口来指定“具有”(或“可以做”)关系,而“是a”则使用继承来实现。

可以在.NET Framework开发人员指南的Choosing Between Classes and Interfaces部分找到相关的基本原理:

  

接口定义实施者必须提供的一组成员的签名。接口无法为成员提供实现细节。

因此,由于您的“程序员”和“工程师”示例类很可能具有自己的特定功能,因此使用继承更适合实现它们。

答案 4 :(得分:2)

实际上,这就是为什么大多数界面都是功能而不是名称,所以你有

IComparable,ITestable,IEnumerable

人类,动物,狗等

无论如何,正如已经提到的那样你必须务实,我在编码时有一个通用规则:永远不要让概念,惯例或标准实践在完成工作的过程中,最好是务实而不是学术。

所以,如果您确定界面真的更适合您的设计,那就去吧,不要担心这个问题。

相关问题