为什么抽象类实现接口?

时间:2015-08-21 23:29:02

标签: c# oop

我看到抽象类实现接口的代码?为什么要这么做?我们可以直接将它们放在接口中,而不是将抽象成员放在接口中吗?

为什么会写这样的代码?目的或需求是什么?

4 个答案:

答案 0 :(得分:6)

接口只是与其他代码的契约。另一方面,抽象类可以更多。他们可以:

  • 拥有数据成员(字段)
  • 有实际的代码实现
  • 从另一个基类派生
  • 有受保护的成员

在模板方法模式中可以找到一个用例的好例子。您可以拥有命令的界面:

public interface IMyCommand
{
    void Execute();
}

你有一组遵循特定顺序的命令。要强制执行此操作,您可以从抽象基类派生它们:

public abstract class MyTemplateClass : IMyCommand
{
    public void Execute()
    {
         MyProcessFirst();
         MyProcessSecond();
    }

    protected abstract void MyProcessFirst();
    protected abstract void MyProcessSecond();
}

现在,您可以使用模板方法后面的对象派生自抽象基类,而其他对象只需实现该接口。

答案 1 :(得分:2)

  

不是将抽象成员放在界面中,而是直接将它们放在界面中吗?

没有实施我们不能。

因为我们可以在抽象类中使用部分实现,所以我们可以获得重用的所有优点。

我们可以问相反的事情;为什么不只是抽象类而没有接口。

通常,特别是对于抽象类和接口的私有或内部组合,这确实是一种改进。但是抽象类没有的接口仍然有一些优点。

显而易见的是,如果我们拥有或可能拥有不是从该抽象类派生的实现。另一种方法是利用界面中的差异,这是班级无法做到的。

即使我们现在没有任何实现接口但不通过抽象类的情况,如果接口是公共的,那么我们是否可以考虑(YAGNI对公共接口的应用非常不同)改变以删除任何东西)。

与此相关,它可以是一个有用的组合,具有客户端代码可以实现的公共接口以及您的代码,以及您提供的实现中使用的抽象类,因为它包含您的实现的通用性对于其他人的实施而言,这必然是共同的。也许你会允许其他人继承它,而不是坚持它,或者你可能只有internal构造函数,所以只有你的代码可以使用它。

答案 2 :(得分:0)

我看到的一个可能原因是,如果使用抽象类实现接口,则不需要实现抽象类中的所有方法。您可以将这些方法保留为声明(间接实现它,但只是推迟实现)。

答案 3 :(得分:0)

假设您正在开发一个具有接口IStream的流行库,该库在整个库中的各种API中使用。 IStream有以下方法:

int Read(byte[] buffer, int offset, int count);
void Write(byte[] buffer, int offset, int count);

但是,不是让人们直接实现该接口,而是强烈建议他们从您的抽象类Stream继承,该抽象类实现该接口,如下所示:

public abstract int Read(byte[] buffer, int offset, int count);
public abstract void Write(byte[] buffer, int offset, int count);

很多人都会按照您的建议,但不是每个人都会阅读文档,因此有些人会直接实施IStream

现在你的库的下一个版本出来了。你真的很兴奋,因为你要为你的流实现异步操作。因此,您将以下方法添加到IStream

Task<int> ReadAsync(byte[] buffer, int offset, int count);
Task WriteAsync(byte[] buffer, int offset, int count);

现在你去更新你的抽象类以使其编译。你可以将新方法抽象化,但事实证明,有一种不完全疯狂的替代方法(省略错误处理):

public virtual Task<int> ReadAsync(byte[] buffer, int offset, int count)
{
    return Task.Run(() => this.Read(buffer, offset, count));
}

public virtual Task WriteAsync(byte[] buffer, int offset, int count)
{
    return Task.Run(() => this.Write(buffer, offset, count);
}

对于许多真实的流类型,通常会采用不同的方式来处理异步读取和写入,并且它可能比这更有效数倍(这就是为什么它是虚拟的而不是密封的),但是这个可能对大多数消费者来说已经足够好了。

现在让我们来看看这两组人。从抽象类继承的人自动获得IStream.ReadAsyncIStream.WriteAsync的实现,而无需自己编写任何代码。他们的流也可以在新的花哨的异步API中原样使用,而不需要做任何工作,并且它有可能不会吮吸。

另一方面,实现该接口的人现在必须处理编写他们自己的IStream方法的实现,即使他们对使用异步API没兴趣。也许他们抛出NotSupportedException以使错误消失。现在他们需要确保他们不会调用可能有可能调用IStream.ReadAsyncIStream.WriteAsync的任何内容。他们不开心。他们没有按照你的建议去做这件事,但仍然很难不同情。

这是一个抽象类实现接口的一大优势。实际上,有些人可能认为IStream根本不应该存在,并且抽象Stream类应该只显示在IStream所有的API中。盲目猜测:也许这是完全为什么有System.IO.Stream,但没有System.IO.IStream。虽然我个人更喜欢IStream左右。