设计 C# 类库时,什么时候应该选择继承而不是接口? [英] When should I choose inheritance over an interface when designing C# class libraries?

查看:35
本文介绍了设计 C# 类库时,什么时候应该选择继承而不是接口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有许多 Processor 类,它们可以做两种截然不同的事情,但都是从公共代码中调用的(控制反转"情况).

我想知道在决定它们是否都应该全部继承自 BaseProcessor 或实现 IProcessor 时,我应该了解(或了解,对于您的用户而言)哪些设计注意事项作为接口.

解决方案

一般来说,规则是这样的:

  • 继承描述了一种is-a关系.
  • 实现接口描述了一种可以做的关系.

为了更具体地表达这一点,让我们看一个例子.System.Drawing.Bitmapis-an 图像(因此,它继承自 Image 类),但它也可以做 处理,所以它实现了 IDisposable 接口.它还可以做序列化,因此它从ISerializable 接口.

但更实际的是,接口通常用于模拟 C# 中的多重继承.如果您的Processor 类需要从System.ComponentModel.Component 之类的东西继承,那么您别无选择,只能实现一个IProcessor 接口.

事实上,接口和抽象基类都提供了一个约定,指定特定类可以做什么.接口是声明这个契约所必需的,这是一个普遍的误解,但这是不正确的.在我看来,最大的优势是抽象基类允许您为子类提供默认功能.但是,如果没有有意义的默认功能,则没有什么可以阻止您将方法本身标记为 abstract,要求派生类自己实现它,就像它们要实现接口一样.>

对于此类问题的答案,我经常求助于.NET Framework Design Guidelines,其中有关于在类和接口之间进行选择的说法:

<块引用>

一般来说,类是暴露抽象的首选结构.

接口的主要缺点是,在允许 API 演变方面,它们比类灵活得多.一旦你发布了一个接口,它的成员集就永远固定了.对接口的任何添加都会破坏实现接口的现有类型.

一个类提供了更多的灵活性.您可以向已经发布的类添加成员.只要该方法不是抽象的(即,只要您提供该方法的默认实现),任何现有的派生类将继续保持不变的功能.

[ ...]

支持接口的最常见论点之一是它们允许将契约与实现分开.但是,该论点错误地假设您不能使用类将契约与实现分开.抽象类位于与其具体实现分离的程序集中,是实现这种分离的好方法.

他们的一般建议如下:

  • 倾向于定义类而不是接口.
  • 使用抽象类而不是接口来将契约与实现分离.抽象类,如果定义正确,允许契约和实现之间相同程度的解耦.
  • 如果您需要提供值类型的多态层次结构,请务必定义一个接口.
  • 考虑定义接口以实现与多重继承类似的效果.

克里斯·安德森特别同意最后一条原则,并认为:

<块引用>

抽象类型的版本要好得多,并允许未来的可扩展性,但它们也会烧毁你的一个也是唯一的基本类型.当您真正定义两个随时间不变的对象之间的契约时,接口是合适的.抽象基类型更适合为类型族定义公共基.

I have a number Processor classes that will do two very different things, but are called from common code (an "inversion of control" situation).

I'm wondering what design considerations I should be cognicent (or cognizant, for you USsers) of when deciding if they should all inherit from BaseProcessor, or implement IProcessor as an interface.

解决方案

Generally, the rule goes something like this:

  • Inheritance describes an is-a relationship.
  • Implementing an interface describes a can-do relationship.

To put this in somewhat more concrete terms, let's look at an example. The System.Drawing.Bitmap class is-an image (and as such, it inherits from the Image class), but it also can-do disposing, so it implements the IDisposable interface. It also can-do serialization, so it implements from the ISerializable interface.

But more practically, interfaces are often used to simulate multiple inheritance in C#. If your Processor class needs to inherit from something like System.ComponentModel.Component, then you have little choice but to implement an IProcessor interface.

The fact is that both interfaces and abstract base class provide a contract specifying what a particular class can do. It's a common myth that interfaces are necessary to declare this contract, but that's not correct. The biggest advantage to my mind is that abstract base classes allow you provide default functionality for the subclasses. But if there is no default functionality that makes sense, there's nothing keeping you from marking the method itself as abstract, requiring that derived classes implement it themselves, just like if they were to implement an interface.

For answers to questions like this, I often turn to the .NET Framework Design Guidelines, which have this to say about choosing between classes and interfaces:

In general, classes are the preferred construct for exposing abstractions.

The main drawback of interfaces is that they are much less flexible than classes when it comes to allowing for the evolution of APIs. Once you ship an interface, the set of its members is fixed forever. Any additions to the interface would break existing types implementing the interface.

A class offers much more flexibility. You can add members to classes that you have already shipped. As long as the method is not abstract (i.e., as long as you provide a default implementation of the method), any existing derived classes continue to function unchanged.

[ . . . ]

One of the most common arguments in favor of interfaces is that they allow separating contract from the implementation. However, the argument incorrectly assumes that you cannot separate contracts from implementation using classes. Abstract classes residing in a separate assembly from their concrete implementations are a great way to achieve such separation.

Their general recommendations are as follows:

  • Do favor defining classes over interfaces.
  • Do use abstract classes instead of interfaces to decouple the contract from implementations. Abstract classes, if defined correctly, allow for the same degree of decoupling between contract and implementation.
  • Do define an interface if you need to provide a polymorphic hierarchy of value types.
  • Consider defining interfaces to achieve a similar effect to that of multiple inheritance.

Chris Anderson expresses particular agreement with this last tenet, arguing that:

Abstract types do version much better, and allow for future extensibility, but they also burn your one and only base type. Interfaces are appropriate when you are really defining a contract between two objects that is invariant over time. Abstract base types are better for defining a common base for a family of types.

这篇关于设计 C# 类库时,什么时候应该选择继承而不是接口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆