【问题标题】:Is an interface and an abstract class with just virtual abstract methods the same thing?仅具有虚拟抽象方法的接口和抽象类是否相同?
【发布时间】:2011-10-25 20:42:03
【问题描述】:

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

这是一个界面:

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 都会中断,因为我可以为抽象提供一些默认实现,这似乎是一个更好的选择。我错过了什么吗?

【问题讨论】:

标签: c# oop interface abstract-class


【解决方案1】:

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

抽象类

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

接口

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

【讨论】:

  • 如果你声明一个包含所有抽象方法的抽象类,扩展抽象类的类不是必须像接口一样实现所有方法,所以这就是我的困惑。
  • +1 - 虽然从技术上讲,您可以使用扩展方法向接口添加“实现”。 =D
  • @Xaisoft - 是的,派生类需要覆盖所有抽象方法。将抽象类视为接口功能的超集,但不能使用多重继承。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    如果比这更复杂(也就是说,在大多数情况下),将会有更多的类和要提取的常见行为片段。然后你必须开始在类继承和接口实现之间做出一个重要的选择,考虑到以下几点:

    • 选择基类只有一次机会,但可以实现任意数量的接口
    • 如果你想让你的“父母”做任何工作,它需要是一个类而不是一个接口

    等等。

    一般来说,你的类的语义应该指导你——如果“事物”具有“IS A”关系(如 MarathonRunner 到 Athlete),则建议使用继承;如果“事物”具有“我可以履行 A 的合同”(例如,Person to Runner),则建议使用接口实现。

    【讨论】:

      【解决方案4】:

      接口是一个非常有用的功能,与抽象类非常相似,在某些情况下,可以与抽象类交换。

      但是,除非必须这样做,否则不要直接跳到接口(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 上发布了一个关于它的问题,但是,没有人知道这种情况的特殊名称。

      干杯。

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          猜你喜欢
          • 2018-12-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-08-14
          • 2023-03-03
          • 2016-12-14
          • 2010-10-11
          相关资源
          最近更新 更多