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

Tor*_*eli 12 c# abstract-class interface c#-8.0 default-interface-member

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

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

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

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

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

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

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

Ous*_* D. 6

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

这篇文章提到的另外两点好处:


Jul*_*ian 6

概念性的

首先,类和接口之间存在概念上的区别。

  • 一个类应该描述一个“是一个”关系。例如,法拉利是汽车
  • 一个接口应该描述一个类型的契约。例如,汽车有方向盘。

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

好处

  • 因此,从上面开始,您可以重用代码而不引入(概念上错误的)抽象类。
  • 您可以从多个接口继承,而抽象类只是单继承
  • C# 中的接口存在协变和逆变,而不是类
  • 实现接口更容易,因为某些方法具有默认实现。这可以为接口的实现者节省大量工作,但用户不会看到区别:)
  • 但对我来说最重要的是(因为我是库维护者),您可以在不进行重大更改的情况下向接口添加新方法!在 C# 8 之前,如果一个接口是公开发布的,它应该是固定的。因为改变接口可能会破坏很多。

记录器界面

这个例子展示了一些好处。

您可以将一个(过度简化的)记录器接口描述如下:

interface ILogger
{
    void LogWarning(string message);

    void LogError(string message);

    void Log(LogLevel level, string message);
}
Run Code Online (Sandbox Code Playgroud)

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

一个更好的默认界面是:

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

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

    void Log(LogLevel level, string message);
}
Run Code Online (Sandbox Code Playgroud)

现在用户仍然可以使用所有方法,但实现者只需要实现Log. 此外,他可以实施LogWarningLogError

此外,将来您可能希望添加日志级别“灾难性”。在 C#8 之前,您无法在LogCatastrophic不破坏所有当前实现的情况下将方法添加到 ILogger。