接口和抽象类只是虚拟抽象方法是一回事吗?

时间:2011-08-12 14:12:26

标签: c# oop interface abstract-class

如果我有一个包含类似类的项目,有些可能使用相同的实现,但在大多数情况下,它们实现了自己处理接口或抽象类中定义的方法的方式。我试图弄清楚接口/抽象类是否更好。如果你可以使用虚拟抽象方法的抽象类,我就不明白界面了。

这是一个界面:

public interface IAthlete
{
    void Run();
}

这是一个抽象类:

public abstract class Athlete
{
    public abstract void Run();
}

以下是界面的实现:

public class Sprinter : IAthlete
{
    public void Run()
    {
        Console.WriteLine("Running Fast....");
    }
}

以下是抽象类的扩展:

public class MarathonRunner : Athlete
{
    public override void Run()
    {
         Console.Write("Jogging....");
    }
 }

现在,如果我决定向接口或抽象方法添加一个名为Stop的方法,Sprinter和MarathonRunner都会中断,因为我可以提供一些默认的抽象实现,这似乎是一个更好的选择。我错过了什么吗?

5 个答案:

答案 0 :(得分:6)

接口和抽象超类之间有两个主要区别:

抽象类

  • 使用抽象超类
  • 可以实现代码重用
  • 你只能继承一个超类

接口

  • 每个方法都必须在每个子类中实现
  • 一个类可以继承多个接口(多重继承)

答案 1 :(得分:1)

如果您所拥有的只是一个提取的共性,那么两者之间没有实质性区别是完全正确的。但这更像是说“在将1添加到2的情况下,intdouble之间没有区别” - 它的技术上是真的< / em>,但不是一个特别有用的如何思考的指南。

如果比这更复杂(在大多数情况下),将会有更多的类,以及要提取的常见数据块。然后你必须开始在类继承和接口实现之间做出重要的选择,同时考虑以下内容:

  • 您只选择一个基类,但您可以实现任意数量的接口
  • 如果你希望你的'父母'做任何工作,它需要是一个类而不是一个接口

等等。

一般来说,你的课程的语义应该指导你 - “事物”有“IS A”关系(比如MarathonRunner to Athlete),建议继承; “事物”中有“我能完成A的合同”(比如说,Run to Runner),建议使用界面实现。

答案 2 :(得分:0)

接口是一个非常有用的功能,与抽象类非常相似,在某些情况下,可以通过抽象类进行交换。< / p>

但是,不要直接跳到接口,取消你必须(在Java开发人员中非常常见的反模式)。我建议,通过阅读你的例子,坚持抽象类。

大多数时候我只使用接口,当我有几个非相关类时,我需要它们有共同的成员,如果这些类来自同一个基类。

在您的示例中,您在尝试查找在添加基本虚拟方法时如果需要新的stop方法会发生什么。这些可以用不同的方法解决,不是抽象类与接口

有3种选择:

(1)添加一个抽象方法,强制程序员覆盖它,以便实例化对象。

(2)添加一个新的虚拟方法,该方法可以执行某些操作,但不必重写。

(3)添加一个什么都不做的新方法,可能适用于你的情况。

// cannot instantiate an abstract class
public abstract class Athlete
{
    // helper method:
    public /* non-abstract */ void DoNothing()
    {
      // does nothing on purpouse !!!
    }

    // (1) virtual & abstract method, must be overriden
    public abstract void Run();


    // (2) new virtual method, doesn't need to be overriden,
    // but, maybe you dont like what it does
    public virtual void Stop()
    {
       Message.Show("Stop !!!");
    }

    // (3) new virtual method, doesn't need to be overriden,
    // its safe to be called
    public virtual void TakeBreak()
    {
      // works like an abstract virtual method, but, you don't need to override
      DoNothing();
    }
} // class Athlete

// in a non abstract class, you must override all abstract methods
public /* non-abstract */ class Runner: Athlete
{
    public override void Run()
    {
       DoNothing(); 
    }

    public override void Stop()
    {
       DoNothing(); 
    }

    // don't need to override this method
    // public virtual void TakeBreak();

} // class Trekker

// ...

Runner ARunner = new Runner();
ARunner.Run();
ARunner.Stop();
ARunner.TakeBreak();

可能适用于您的示例的第三种虚方法并没有特殊的名称,我已经在stackoverflow上发布了一个关于它的问题,但是,没有人知道这个案例的特殊名称。

干杯。

答案 3 :(得分:0)

接口和抽象类之间的一个重要区别是它们的成员如何处理多代继承。假设有抽象方法BaseFoo的抽象类Bar和方法IFoo的接口Boz; class Foo继承BaseFoo并实现IFoo,而class DerivedFoo继承自Foo

如果DerivedFoo需要覆盖BaseFoo.Bar,则可能会覆盖base.Bar(),如果需要使用其父实施,则覆盖可能会调用Foo。如果Boz使用虚方法隐式实现DerivedFoo,则base.Boz()可以覆盖该方法并调用Foo(覆盖是类的函数而不是接口)但是如果IFoo.Boz明确实现了DerivedFoo,那么IFoo.Boz改变Foo行为的唯一方法就是重新实现它。如果是这样,那么IFoo.Boz DerivedFoo的实施将无法访问,即使在{{1}}的同一接口成员的实施中也是如此。

答案 4 :(得分:-1)

接口是一个很好的方式,因为.NET开发人员当前的共识是,你应该支持组合而不是继承,因此接口是一个更好的策略(想想注入容器以及它们的用途,以及让我们不要开始单元测试)。 类,类可以实现许多接口,但只能从一个类(抽象或其他)继承。结构体也可以实现接口,但不能从其他类继承。 在运行时级别,接口更有效,因为运行时不必遍历继承堆栈以计算调用特定成员的多态含义。