默认接口实现。现在,抽象类和接口之间有什么深刻的意义差异?

时间:2017-12-01 21:00:23

标签: c# .net interface abstract-class c#-8.0

我知道抽象类是一种无法实例化的特殊类。抽象类只是被分类(继承自)。换句话说,它只允许其他类继承它但不能实例化。优点是它为所有子类强制执行某些层次结构。简单来说,它是一种强制所有子类继承相同层次结构或标准的契约。

我也知道界面不是一个类。它是由Interface这个词定义的实体。接口没有实现;它只有签名或换言之,只是没有正文的方法的定义。作为与Abstract类相似的一个,它是一个契约,用于为所有子类定义层次结构,或者它定义特定的方法集及其参数。它们之间的主要区别在于类可以实现多个接口,但只能从一个抽象类继承。由于C#不支持多重继承,因此接口用于实现多重继承。

当我们创建一个接口时,我们基本上创建了一组方法,没有必须被实现的类覆盖的任何实现。它的优点是它为类提供了一种方法,使其成为两个类的一部分:一个来自继承层次结构,另一个来自接口。

当我们创建一个抽象类时,我们正在创建一个可能有一个或多个已完成方法的基类,但至少有一个或多个方法未完成并声明为抽象。如果抽象类的所有方法都未完成,那么它与接口相同。

BUT BUT BUT

我注意到我们将在C#8.0中使用默认接口方法

也许我问它因为我只有1 - 2年的编程经验,但现在抽象类和界面之间的主要区别是什么?

我知道我们不能在界面中创建状态,它们之间只有一个区别吗?

6 个答案:

答案 0 :(得分:5)

除了显而易见的抽象类可以具有状态和接口之外的事实之外,两者之间存在很大差异。默认方法或也称为虚拟扩展方法实际上已在Java中提供了一段时间。默认方法的主要驱动因素是 interface evolution ,这意味着能够在未来版本中向接口添加方法,而不会破坏与该接口的现有实现的源或二进制兼容性。

这个post提到的另外几个好点:

答案 1 :(得分:3)

概念性

首先,类和接口之间在概念上有所不同。

  • 类应描述“是”关系。例如。法拉利就是汽车
  • 接口应描述一种类型的合同。例如。汽车有方向盘。

当前,即使没有“是”关系,抽象类有时也会用于代码重用。这污染了OO设计。例如。 FerrariClass继承自CarWithSteeringWheel

好处

  • 因此,从上面开始,您可以重用代码而无需引入(在概念上错误的)抽象类。
  • 您可以从多个接口继承,而抽象类仅是单个继承
  • 在C#中,接口而不是类之间存在协变和矛盾
  • 实现接口更容易,因为某些方法具有默认实现。这样可以为接口的实现程序节省很多工作,但用户看不到区别:)
  • 但是对我来说最重要的(因为我是一名图书馆维护者),您可以在不做重大更改的情况下向接口添加新方法!在C#8之前,如果接口是公开发布的,则应该对其进行修复。因为更改界面可能会导致很多问题。

记录器界面

此示例显示了一些好处。

您可以描述一个(过于简化的)记录器界面,如下所示:

interface ILogger
{
    void LogWarning(string message);

    void LogError(string message);

    void Log(LogLevel level, string message);
}

然后,该界面的用户可以使用LogWarningLogError轻松地将其记录为警告和错误。但是缺点是实现者必须实现所有方法。

更好的默认界面是:

interface ILogger
{
    void LogWarning(string message) => Log(LogLevel.Warning, message);

    void LogError(string message) => Log(LogLevel.Error, message);

    void Log(LogLevel level, string message);
}

现在,用户仍然可以使用所有方法,但是实现者仅需要实现Log。另外,他可以实现LogWarningLogError

此外,将来您可能希望添加logLevel“ Catastrophic”。在C#8之前,您不能在不破坏所有当前实现的情况下将方法LogCatastrophic添加到ILogger。

答案 2 :(得分:2)

仍然使界面独特的另一件事是covariance / contravariance

说实话,从未发现自己处于违约的情况。在界面是解决方案。我对此持怀疑态度。

答案 3 :(得分:0)

我想到的唯一主要区别是你仍然可以重载接口永远不会有的抽象类的默认构造函数。

abstract class LivingEntity
{
    public int Health
    {
        get;
        protected set;
    }


    protected LivingEntity(int health)
    {
        this.Health = health;
    }
}

class Person : LivingEntity
{
    public Person() : base(100)
    { }
}

class Dog : LivingEntity
{
    public Dog() : base(50)
    { }
}

答案 4 :(得分:0)

两个主要区别:

  • 抽象类可以具有状态,但是接口不能具有状态。
  • 类型可以从单个抽象类派生,但是可以实现多个接口。

关于默认修饰符,还有一些其他小的区别。

答案 5 :(得分:0)

抽象类和新的默认接口方法都有其适当的用途。

A。原因

尚未引入默认接口方法来替代抽象类。

What's new in C# 8.0状态:

  

此语言功能使API作者可以在以后的版本中向接口添加方法,而不会破坏与该接口的现有实现的源代码或二进制兼容性。现有实现继承默认实现。

     

此功能还使C#可以与支持类似功能的Android或Swift的API进行互操作。默认界面方法还可以启用类似于“特征”语言功能的方案。

B。功能上的差异

抽象类和接口之间(即使使用默认方法)仍然存在显着差异。

以下是接口仍然 无法执行的一些事情,而抽象类可以可以实现的事情:

  • 有一个构造函数,
  • 保持状态
  • 继承自非抽象类,
  • 具有私有方法。

C。设计

虽然默认接口方法使接口更加强大,但抽象/基类和接口仍然代表着根本不同的关系。

  

(来自When should I choose inheritance over an interface when designing C# class libraries?

     
      
  • 继承描述了is-a关系。
  •   
  • 实现接口描述了可以做的关系。
  •