只有同步内容的异步函数的最佳实践?

Best practice for async functions with only synchronous contents?

假设我有一个虚拟成员函数异步调用:

class Command {
    public abstract Task<object> Execute();  // Meant to be async for implementations
}
class Caller {
    Command command;
    public async Task Call() {
        await command.Execute();  // Command.Execute is always called with `await`
    }
}

对于大多数情况,实现只是一个普通的异步逻辑:

class NormalCase : Command {
    public override async Task<object> Execute() {
        await ...;  // Some async works
        return ...;
    }
}

但确实存在一些派生实现只是为了同步完成的情况——即不需要 await

class SyncCase : Command {
    public override async Task<object> Execute() => 1;  // Just a synchronous value
}

这当然有效,但会出现编译警告(CS1998)。 放置 #pragma 选项可以抑制警告,但在许多单独的文件中都有实现,我必须在所有地方放置 #pragmas。 (非常感谢,Unity!) 所以让我们把这个解决方案放在一边。

我还尝试了其他几种可能的解决方案,但其中 none 效果很好:

class Solution1 {
    public override async Task<object> Execute() {
        await Task.Delay(1);  // Just NASTY
        return 1;
    }
}
class Solution2 {
    public override Task<object> Execute() => 1;  // Will cause upstream calls to break
}
class Solution3 {
    public override Task<object> Execute() =>
        new Task<object>(() => 1);  // Same, it breaks upstream logic
}

鉴于这种情况,最佳做法是什么?

答案是——不要。你在向函数的消费者撒谎。想象一下,调用一个您认为是异步的方法在后台执行某些操作,却发现您的 UI 冻结了 10 秒。

当且仅当结果已经可用时,使用 Task.FromResult

class StaticCase : Command {
    
    public override Task<object> Execute() => Task.FromResult((object)1); 
}

否则通过使用 Task.Run() 到 运行 单独线程中的阻塞代码使方法异步:

class BlockingCase : Command {
    
    public override async Task<object> Execute() 
    {
        var result=await Task.Run(()=>SomeHeavyWork());
        return result;
    }
}