Почему Task и Task не sealed?
Почему майкрософт не убрали возможность наследования от Task и Task? Если не sealed, значит есть ситуации когда можно/правильно пронаследоваться от этого класса. Какие это могут быть в теории ситуации?
Ответы (2 шт):
А зачем их запечатывать? Как наследование от тасков может их сломать (если специально не стараться)?
Практика в .NET, хоть и на первый взгляд нелогична, но такова:
- Методы НЕ виртуальны по умолчанию
- Классы НЕ запечатаны по умолчанию
Task и Task<T> просто следуют этой же практике.
На деле же, эта практика вполне логична. Вы можете хотеть запечатать класс, когда у вас есть более высокоуровневый класс с виртуальными методами, которые вы переопределяете в своем классе, но дальнейшее наследование от вашего класса не является каким-то логичным и/или довольно тривиальным.
Кейс наследования тоже довольно просто придумать. Если вам нужен какой-нибудь, например, Task<Dictionary<MyClass, Dictionary<string, int>>, то вы можете написать MyTask : Task<Dictionary<MyClass, Dictionary<string, int>> и сократить себе запись в коде.
Задачи не запечатаны по той причине, что наследование от Task активно используется в недрах фреймворка. Вот примеры:
Task.CancellationPromise<T>- задача, возвращаемая методомTask<T>.WaitAsyncTask.DelayPromise- задача, возвращаемая методомTask.DelayTask.WhenAllPromise- задача, возвращаемая методомTask.WhenAllStream.ReadWriteTask- задача, возвращаемая стандартными асинхронными методами потоков ввода-вывода
В принципе, от любого из этих наследников при желании можно избавиться, обойдясь внешним API (тот же WaitAsync неоднократно реализовывался разными костылями вокруг обычного API, а для всего остального есть TaskCompletionSource), однако использование особого внутреннего (internal) API совместно с наследованием позволяет коду из стандартной библиотеки аллоцировать меньше объектов.
Вспомним стандартный полифил для WaitAsync, чаще называемый WithCancellation:
public static async Task WithCancellation(this Task task, CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
{
if (task != await Task.WhenAny(task, tcs.Task).ConfigureAwait(false))
{
throw new OperationCanceledException(cancellationToken);
}
await task; // already completed; propagate any exception
}
}
Он создаёт аж 4 новых объекта в куче: TaskCompletionSource, CancellationTokenRegistration (это не объект, но внутри объект всё равно есть), результат WhenAny, и конечный автомат для async-метода.
Создателям же CancellationPromise удалось обойтись только самим CancellationPromise и CancellationTokenRegistration.