抽象类与接口

时间:2009-09-24 21:26:55

标签: c#

我对C#中抽象类的使用有点困惑。在C ++中,定义一个继承抽象类的类可以遵循的模板是有意义的。但是,在C#中,Interface不具备相同的用途吗?

确实,抽象类可以具有接口不提供的默认实现。因此,如果实现不需要包含在基类中,那么最好选择接口吗?

14 个答案:

答案 0 :(得分:20)

我仍然希望提供一个接口的默认抽象实现,假设它是一个实质性的接口(并且它是有意义的)。你永远不知道什么时候你可以在接口上添加一些东西,这个接口有一个简单的默认实现,可以包含并免费提供给从抽象基类继承的任何人。

答案 1 :(得分:19)

这个CodeProject article有很多关于两者之间差异的信息,包括比较和对比每个特征的表格。

接口定义类之间的契约 - 类相互调用的方式。一个类可以实现多个接口,但只能从一个抽象类继承。

答案 2 :(得分:10)

  

确实,抽象类可以具有接口不提供的默认实现。因此,如果实现不需要包含在基类中,那么最好选择接口吗?

是:)。如果在基类中实现某些方法是有意义的,这些方法对于所有固有类是通用的,那么你应该使用一个抽象类。如果基类只用于定义接口,但继承的类之间没有通用逻辑,则使用接口。

答案 3 :(得分:6)

关于你的第一个问题,是的。

对于你的第二个答案,我会给你一些我所遵循的提示。

  • 结合使用抽象类和接口来优化您的设计权衡。

使用抽象类

  • 在创建将广泛分发或重用的类库时 - 特别是对客户端,使用优先于接口的抽象类;因为,它简化了版本控制。

  • 使用抽象类为一系列类型定义公共基类。

  • 使用抽象类提供默认行为。

  • 子类只是类逻辑上所属的层次结构中的基类。

使用界面

  • 创建可以随意更改的独立项目时,请使用优先于抽象类的接口;因为它提供了更多的设计灵活性。

  • 使用接口在不进行子类化的情况下引入多态行为,并为多重继承建模 - 允许特定类型支持多种行为。

  • 使用接口为值类型设计多态层次结构。

  • 在真正意图使用不可变合同时使用接口。

  • 精心设计的界面定义了一系列非常具体的功能。拆分包含不相关功能的接口。

答案 4 :(得分:5)

您可以实现任意数量的接口,但只能继承一个类。因此,C#中的类和接口是完全不同的野兽,你不能互换使用它们。在C#中,抽象类仍然是类,而不是接口。

答案 5 :(得分:5)

接口和抽象类服务于不同的目标。接口用于声明类的合同,而抽象类用于共享公共实现。

如果只使用抽象类,则类不能从其他类继承,因为C#不支持多重继承。如果您只使用接口,则您的类不能共享公共代码。

public interface IFoo
{
    void Bar();
}

public abstract class FooBase : IFoo
{
    public abstract void Bar()
    {
        // Do some stuff usually required for IFoo.
    }
}

现在我们可以在各种情况下使用接口和基础实现。

public class FooOne : FooBase
{
    public override void Bar()
    {
        base.Bar(); // Use base implementation.

        // Do specialized stuff.
    }
}

public class FooTwo : FooBase
{
    public override void Bar()
    {
        // Do other specialized stuff.

        base.Bar(); // Use base implementation.

        // Do more specialized stuff.
    }
}

// This class cannot use the base implementation from FooBase because
// of inheriting from OtherClass but it can still implement IFoo.
public class FooThree : OtherClass, IFoo
{
    public virtual void Bar()
    {
        // Do stuff.
    }
}

答案 6 :(得分:4)

如果您没有任何默认/公共代码,请使用界面。

抽象类也可以作为模板,它定义了某些算法的步骤和调用它们的顺序,派生类提供了这些步骤的实现:

public abstract class Processor
{
  // this is the only public method
  // implements the order of the separate steps
  public void Process()
  {
    Step1();
    Step2();
    //... 
  }
  // implementation is provided by derived classes
  protected abstract void Step1();
  protected abstract void Step2();
}

答案 7 :(得分:1)

虽然没有实现的抽象类与接口等效,但接口和抽象类用于不同的事情。

接口可以用于最普遍意义上的多态性。例如,ICollection用于定义所有集合的接口(有很多)。这里定义了您想要对某种类型执行的操作。还有许多其他用途(例如可测试性,依赖注入等)。此外,接口可以混合使用,这在概念和技术上都有效。

抽象类更多地与模板行为有关,其中虚拟方法是“填补空白”的地方。显然你不能混合抽象类(至少不能在C#中)。

答案 8 :(得分:1)

在C#中,使用抽象类的一个巨大威慑是你只能使用一个。使用接口,您可以不限制实现的基类。为此,即使我创建了一个抽象基类来帮助实现,我总是使用一个接口。

基本抽象类的另一个烦恼通常是它们倾向于依赖模板参数。这可能使您的其余代码很难使用。对此的简单回答是提供一个与抽象类进行通信的接口,而不需要知道模板类的类型参数。

其他人似乎更快地输入答案,但请允许我总结一下......

  

使用界面。如果需要共享实现,还可以创建一个提供常见实现细节的抽象基类。

答案 9 :(得分:1)

请注意,使用C#3,您可以通过使用扩展方法为接口提供默认行为。但是有一些限制,抽象类仍然有它们的位置。

答案 10 :(得分:1)

建模时遵循的规则是: 类(包括抽象)和结构模型实体。接口模型行为。 实现接口的实体可以被视为展示接口(合同)暴露的行为。

答案 11 :(得分:1)

在一些答案中暗示了这一点,但没有明确说明。

您可以实现多个接口并且只从一个基类继承,就好像它们是同一个硬币的两面一样,这不是一个很好的方法来查看它。

不要将接口视为对象层次结构的一部分。它们通常只是您的真实对象层次结构可以声明为实现的功能的一小部分(或者至少是特定的,如果不是很小)。以IDisposable为例。如果你是那个写作的人,你会问自己它应该是一个抽象类还是一个接口?很明显,在这种情况下,它们是两个完全不同的东西。我想要一次性使用。想想ICloneable和IEnumerable。您可以在类中实现这些,而无需尝试使您的类派生自一些不相关的类,如List或Array。或者拿IEnumerator。只需为对象提供MoveNext类型的视图。我的类可以提供该功能,而不必笨拙地从与我的类无关的其他顺序集合数据类型派生。

答案 12 :(得分:0)

我总是喜欢接口,只要基类没有一些真正的“重型”实现,这将为实现者节省大量时间。 这个.net只允许一个基类继承,迫使你的用户继承是一个巨大的限制。

答案 13 :(得分:0)

您应该始终更喜欢编程到接口而不是具体类。

如果您还想要一个默认实现,您仍然可以创建一个实现您的接口的基类。