我最近接受了两次电话采访,被问及接口类和抽象类之间的区别。我已经解释了我能想到的每一个方面,但似乎他们在等我提一些具体的事情,我不知道是什么。

根据我的经验,我认为以下是正确的。如果我遗漏了一个要点,请告诉我。

接口:

接口中声明的每个方法都必须在子类中实现。接口中只能存在事件、委托、财产(C#)和方法。一个类可以实现多个接口。

抽象类:

子类只能实现抽象方法。抽象类可以具有具有实现的普通方法。除了事件、委托、财产和方法之外,抽象类还可以有类变量。由于C#中不存在多重继承,一个类只能实现一个抽象类。

在这之后,面试官提出了一个问题:“如果你有一个只有抽象方法的抽象类呢?这和接口有什么不同?”我不知道答案,但我认为这是上面提到的继承,对吧?另一位面试官问我,“如果你在接口中有一个公共变量,那会和抽象类中有什么不同?”我坚持认为你不能在接口中使用公共变量。我不知道他想听什么,但他也不满意。

另请参阅:

何时使用接口而不是抽象类,反之亦然接口与抽象类如何决定使用抽象类和接口?接口和抽象类之间的区别是什么?


当前回答

接口定义服务或服务集的契约。它们以水平方式提供多态性,因为两个完全不相关的类可以实现相同的接口,但可以互换地用作它们实现的接口类型的参数,因为这两个类都承诺满足接口定义的服务集。接口没有提供实现细节。

抽象类为其子类和可选的部分实现定义了基结构。抽象类以垂直但定向的方式提供多态性,因为继承抽象类的任何类都可以被视为该抽象类的实例,而不是相反。抽象类可以而且经常包含实现细节,但不能单独实例化——只有它们的子类可以“更新”。

注意,C#也允许接口继承。

其他回答

接口类型与抽象基类

改编自Pro C#5.0和.NET 4.5 Framework书籍。

接口类型似乎与抽象基类非常相似。回忆起当类被标记为抽象时,它可以定义任意数量的抽象成员来提供所有派生类型的多态接口。然而,即使类确实定义了一组抽象成员,还可以自由定义任意数量的构造函数、字段数据、非抽象成员(使用另一方面,接口只包含抽象成员定义。抽象父类建立的多态接口有一个主要限制因为只有派生类型支持抽象父级定义的成员。然而,在更大范围内软件系统,开发多个没有共同父级的类层次结构是非常常见的超出System.Object。假定抽象基类中的抽象成员仅适用于派生类型,我们无法在不同的层次结构中配置类型以支持相同的多态性界面例如,假设您定义了以下抽象类:

public abstract class CloneableType
{
// Only derived types can support this
// "polymorphic interface." Classes in other
// hierarchies have no access to this abstract
// member.
   public abstract object Clone();
}

给定此定义,只有扩展CloneableType的成员才能支持Clone()方法如果创建一组不扩展此基类的新类,则无法获得多态界面。此外,您可能还记得C#不支持类的多重继承。因此,如果您想创建一辆小型货车,它是一辆汽车,并且是一种可克隆类型,那么您无法做到:

// Nope! Multiple inheritance is not possible in C#
// for classes.
public class MiniVan : Car, CloneableType
{
}

正如您所猜测的,接口类型起到了拯救作用。定义接口后,可以可以由任何类或结构、任何层次结构、任何命名空间或任何程序集中实现(用任何.NET编程语言编写)。正如您所看到的,接口是高度多态的。考虑System命名空间中定义的名为ICloneable的标准.NET接口。这接口定义了一个名为Clone()的方法:

public interface ICloneable
{
object Clone();
}

第二个问题的答案:接口中定义的公共变量默认为静态final,而抽象类中的公共变量为实例变量。

抽象类处理有效地打包类功能,而接口用于意图/契约/通信,并且应该与其他类/模块共享。

使用抽象类作为契约和(部分)契约实现者违反了SRP。使用抽象类作为契约(依赖关系)限制了创建多个抽象类以获得更好的重用性。

在下面的示例中,使用抽象类作为OrderManager的合同会产生问题,因为我们有两种不同的处理订单的方式-基于客户类型和类别(客户可以是直接或间接的,也可以是黄金或白银)。因此,接口用于契约,抽象类用于不同的工作流实施

public interface IOrderProcessor
{
    bool Process(string orderNumber);
}

public abstract class CustomerTypeOrderProcessor: IOrderProcessor
{
    public bool Process(string orderNumber) => IsValid(orderNumber) ? ProcessOrder(orderNumber) : false;

    protected abstract bool ProcessOrder(string orderNumber);

    protected abstract bool IsValid(string orderNumber);
}

public class DirectCustomerOrderProcessor : CustomerTypeOrderProcessor
{
    protected override bool IsValid(string orderNumber) => string.IsNullOrEmpty(orderNumber); 

    protected override bool ProcessOrder(string orderNumber) => true; 
}

public class InDirectCustomerOrderProcessor : CustomerTypeOrderProcessor
{
    protected override bool IsValid(string orderNumber) => orderNumber.StartsWith("EX");

    protected override bool ProcessOrder(string orderNumber) => true;
}

public abstract class CustomerCategoryOrderProcessor : IOrderProcessor
{
    public bool Process(string orderNumber) => ProcessOrder(GetDiscountPercentile(orderNumber), orderNumber);

    protected abstract int GetDiscountPercentile(string orderNumber);

    protected abstract bool ProcessOrder(int discount, string orderNumber);
}

public class GoldCustomer : CustomerCategoryOrderProcessor
{
    protected override int GetDiscountPercentile(string orderNumber) => 15;

    protected override bool ProcessOrder(int discount, string orderNumber) => true;

}

public class SilverCustomer : CustomerCategoryOrderProcessor
{
    protected override int GetDiscountPercentile(string orderNumber) => 10;

    protected override bool ProcessOrder(int discount, string orderNumber) => true;

}

public class OrderManager
{
    private readonly IOrderProcessor _orderProcessor;// Not CustomerTypeOrderProcessor or CustomerCategoryOrderProcessor 

    //Using abstract class here would create problem as we have two different abstract classes
    public OrderManager(IOrderProcessor orderProcessor) => _orderProcessor = orderProcessor;
}

简而言之:抽象类用于建模类似类的类层次结构(例如,Animal可以是抽象类,Human、Lion、Tiger可以是具体的派生类)

AND

接口用于2个相似/非相似类之间的通信,这些类不关心实现接口的类的类型(例如,Height可以是接口属性,它可以由Human、Building、Tree实现。无论你能不能吃东西,你能不能游泳,你能死什么都不重要。重要的是你需要有Height(在你的类中实现))。

1) 接口可以被视为纯抽象类,这是相同的,但尽管如此,实现接口和从抽象类继承并不相同。当你从这个纯抽象类继承时,你定义了一个层次结构->继承,如果你实现了你不需要的接口,你可以实现任意多的接口,但是你只能从一个类继承。

2) 您可以在接口中定义属性,因此实现该接口的类必须具有该属性。

例如:

  public interface IVariable
  {
      string name {get; set;}
  }

实现该接口的类必须具有这样的属性。