Task.Delay 如果未标记为异步,如何等待?

How is Task.Delay awaitable if it's not marked async?

我正在查看 Task.Delay(int) 在 ILSpy 中反编译:

// System.Threading.Tasks.Task
[__DynamicallyInvokable]
public static Task Delay(int millisecondsDelay)
{
    return Task.Delay(millisecondsDelay, default(CancellationToken));
}

这个方法像await Task.Delay(5000);一样使用,智能感知甚至说“(等待)”:

那么 Task.Delay(int) 怎么没有被标记为 async (public static async Task Delay(int millisecondsDelay))?

等待的是 Task Task.Delay return。 returning Task/Task<TResult> 的每个方法都是可等待的。 async 只是一个实现细节,允许您在该方法及其生成的整个状态机中使用 await

更一般地说,每个 thing 都有一个 GetAwaiter 方法 (extension methods count as well) return somethingIsCompleted, OnCompletedGetResult 可以等待。

例如,Task.Yield returns YieldAwaitable 不是 Task,看起来像这样:

public struct YieldAwaiter : ICriticalNotifyCompletion, INotifyCompletion
{
    public void OnCompleted(Action continuation);
    public void UnsafeOnCompleted(Action continuation);
    public void GetResult();
    public bool IsCompleted { get; }
}

*UnsafeOnCompleted 这里只是一个优化,await 没有它也可以。

重要的是要注意编译器在这种情况下(与其他情况相同,例如 GetEnumerator 用于 foreach)不期望接口或基 class。它基本上使用 duck typing(即 "if it walks like a duck...")并简单地寻找一个 GetAwaiter 方法 return 任何东西(无关紧要什么类型或接口,或者如果它是 class 或结构)具有其他 3 个成员(IsCompletedOnCompletedGetResult

例如,你可以这样await "bar"编译(当然会在运行时失败):

public static Awaiter GetAwaiter(this string s)
{
    throw new NotImplementedException();
}
public abstract class Awaiter : INotifyCompletion
{
    public abstract bool IsCompleted { get; }
    public abstract void GetResult();
    public abstract void OnCompleted(Action continuation);
}

总而言之,您不需要 async 到 return 等待的方法,而且 . Net 框架不使用它并显式 return a Task.