it-swarm.cn

什么时候在C#中使用抽象类而不是具有扩展方法的接口?

“抽象类”和“接口”是相似的概念,其中接口是两者中比较抽象的。一个区别因素是抽象类在需要时为派生类提供方法实现。但是,在C#中,最近引入的扩展方法已减小了这一差异因素,该扩展方法使得可以为接口方法提供实现。另一个区别因素是,一个类只能继承一个抽象类(即没有多重继承),但是可以实现多个接口。这使接口的限制更少,更加灵活。 因此,在C#中,什么时候应该使用抽象类而不是具有扩展方法的接口?

接口+扩展方法模型的一个显着示例是LINQ,其中为通过多种扩展方法实现IEnumerable的任何类型提供了查询功能。

70
Gulshan

当您需要实现类的一部分时。我使用的最好的示例是 template method 模式。

public abstract class SomethingDoer
{
    public void Do()
    {
        this.DoThis();
        this.DoThat();
    }

    protected abstract void DoThis();
    protected abstract void DoThat();
}

因此,您可以定义调用Do()时将要采取的步骤,而无需了解如何实现这些细节。派生类必须实现抽象方法,而不是Do()方法。

扩展方法不一定满足方程式的“必须是类的一部分”部分。此外,iirc扩展方法在范围上不能(似乎)是公共的。

编辑

这个问题比我最初认为的要有趣。经过进一步检查,Jon Skeet 在(SO)上回答了 这样的问题,倾向于使用接口+扩展方法。此外, 潜力downside 对这样设计的对象层次结构使用反射。

就我个人而言,我很难看到改变当前普遍做法的好处,但是这样做的缺点很少甚至没有。

应该注意的是,可以通过Utility类以多种语言进行这种编程。扩展仅提供语法糖,以使方法看起来像它们属于该类。

63
Steven Evers

这就是您要建模的

抽象类通过继承性工作。它们只是特殊的基类,它们对一些is-a关系进行建模。

例如,狗动物,因此我们有

class Dog : Animal { ... }

由于实际上创建通用全动物(看起来像什么)没有意义,因此我们将此Animal基类abstract-但它仍然是基类。如果没有Dog,则Animal类没有任何意义。

Interfaces另一方面是另外一个故事。它们不使用继承,而是提供多态性((也可以通过继承实现))。他们不为is-a关系建模,但更多的关系确实支持

以例如IComparable-一个支持的对象与另一个对象进行比较。

类的功能不依赖取决于其实现的接口,该接口仅提供访问该功能的通用方法。我们仍然可以在不让Dispose实现Graphics的情况下_FileStreamIDisposable接口只是将方法关联在一起

原则上,可以像扩展一样添加和删除接口,而无需更改类的行为,而只是丰富了对它的访问。但是,您不能删除基类,因为该对象将变得毫无意义!

25
Dario

我刚刚意识到,多年来我没有使用抽象类(至少没有创建过)。

抽象类是接口和实现之间有点奇怪的野兽。抽象类中有些东西使我的蜘蛛感觉有些刺痛。我认为,不应以任何方式(或进行任何搭配)“暴露”接口背后的实现。

即使在实现接口的类之间需要通用的行为,我也会使用独立的帮助器类,而不是抽象类。

我会去寻找接口。

3
Maglob

恕我直言,我认为这里混合了一些概念。

类使用某种继承,而接口使用另一种继承。

两种继承都很重要和有用,每种继承都有其优缺点。

让我们从头开始。

关于接口的故事早在C++时代就开始了。在1980年代中期,当开发C++时,尚未意识到接口作为另一种类型的想法(它会及时出现)。

C++只有一种继承,即类使用的继承,称为实现继承。

为了能够应用GoF Book的建议:“为接口编程,而不是为实现编程”,C++支持抽象类和多个实现继承,以便能够执行C#中的接口能够(并且非常有用!) 。

C#引入了一种新的类型,接口以及一种新的继承(即接口继承),以至少两种不同的方式来支持“为接口编程,而不是为实现编程”,其中有一个重要的警告:仅C#通过接口继承(而不是实现继承)支持多重继承。

因此,GoF建议使用C#中的接口背后的体系结构原因。

希望对您有所帮助。

亲切的问候,加斯顿

2
Gastón E. Nusimovich

将扩展方法应用于接口对于在可能仅共享一个公共接口的类之间应用公共行为很有用。这样的类可能已经存在,并且通过其他方式无法扩展。

对于新设计,我倾向于在接口上使用扩展方法。对于类型的层次结构,扩展方法提供了一种扩展行为的方法,而无需打开整个层次结构进行修改。

但是,扩展方法无法访问私有实现,因此在层次结构的顶部,如果我需要将私有实现封装在公共接口之后,那么抽象类将是唯一的方法。

1
Ed James

我认为在C#中不支持多重继承,这就是为什么在C#中引入接口的概念。

在抽象类的情况下,也不支持多重继承。因此,很容易决定是否要使用抽象类或接口。

0
Beginner