Предположим, вы разрабатываете популярную библиотеку с интерфейсом 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
должен отображаться только во всех API-интерфейсах, где вместо этого должно было быть IStream
. Слепая догадка: может быть, это точно почему есть System.IO.Stream, но нет System.IO.IStream
. Хотя я лично предпочел бы иметь IStream
.
Удивительный .. очень хороший пример. Спасибо. – user2683098