我看到抽象类实现接口的代码?为什么要这么做?我们可以直接将它们放在接口中,而不是将抽象成员放在接口中吗?
为什么会写这样的代码?目的或需求是什么?
答案 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.ReadAsync
和IStream.WriteAsync
的实现,而无需自己编写任何代码。他们的流也可以在新的花哨的异步API中原样使用,而不需要做任何工作,并且它有可能不会吮吸。
IStream
方法的实现,即使他们对使用异步API没兴趣。也许他们抛出NotSupportedException以使错误消失。现在他们需要确保他们不会调用可能有可能调用IStream.ReadAsync
或IStream.WriteAsync
的任何内容。他们不开心。他们没有按照你的建议去做这件事,但仍然很难不同情。
这是一个抽象类实现接口的一大优势。实际上,有些人可能认为IStream
根本不应该存在,并且抽象Stream
类应该只显示在IStream
所有的API中。盲目猜测:也许这是完全为什么有System.IO.Stream,但没有System.IO.IStream
。虽然我个人更喜欢IStream
左右。