原则上,如果您愿意,您可以通过绕过外部 API 来摆脱这些继承者中的任何一个(相同的 WaitAsync 被围绕常规 API 的各种拐杖重复实现,而对于其他一切都有 TaskCompletionSource),但是,使用一个特殊的internal(内部)API 与继承一起允许标准库中的代码分配更少的对象。
回想一下标准的 WaitAsync polyfill,通常称为 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
}
}
为什么要把他们封起来?从任务继承如何破坏它们(如果你不专门尝试)?
.NET 中的做法,虽然乍一看不合逻辑,但实际上是这样的:
Task
并Task<T>
遵循相同的做法。其实,这种做法是很合乎逻辑的。当您有一个更高级别的类时,您可能想要密封一个类,该类具有您在类中覆盖的虚拟方法,但是从您的类进一步继承是不合逻辑的和/或相当微不足道的。
继承案例也很容易想出。例如,如果您需要一些,
Task<Dictionary<MyClass, Dictionary<string, int>>
那么您可以编写MyTask : Task<Dictionary<MyClass, Dictionary<string, int>>
并缩短代码中的条目。任务不是密封的,因为在框架的内部积极使用了从 Task 的继承。这里有些例子:
Task.CancellationPromise<T>
- 方法返回的任务Task<T>.WaitAsync
Task.DelayPromise
- 方法返回的任务Task.Delay
Task.WhenAllPromise
- 方法返回的任务Task.WhenAll
Stream.ReadWriteTask
- 由 I/O 流的标准异步方法返回的任务原则上,如果您愿意,您可以通过绕过外部 API 来摆脱这些继承者中的任何一个(相同的 WaitAsync 被围绕常规 API 的各种拐杖重复实现,而对于其他一切都有 TaskCompletionSource),但是,使用一个特殊的internal(内部)API 与继承一起允许标准库中的代码分配更少的对象。
回想一下标准的 WaitAsync polyfill,通常称为 WithCancellation:
它在堆上创建了多达 4 个新对象:
TaskCompletionSource
,CancellationTokenRegistration
(这不是一个对象,但里面仍然有一个对象),结果WhenAny
,以及异步方法的状态机。创作者
CancellationPromise
设法仅靠自己CancellationPromise
和CancellationTokenRegistration
.