Await或Task.FromResult

我有一个服务可以说,

public interface ISomeService { Task DoSomeExpensiveCheckAsync(string parameter); } 

我有这个课程来消费这项服务。 它只需要做一些简单的空检查,然后返回服务响应。

 public class SomeServiceConsumer { private readonly ISomeService _serviceClient; public SomeServiceConsumer(ISomeService serviceClient) { _serviceClient = serviceClient; } public async Task DoSomething1Async(string someParameter) { if (string.IsNullOrWhiteSpace(someParameter)) { return false; } return await _serviceClient.DoSomeExpensiveCheckAsync(someParameter); } //No async or await keywords public Task DoSomething2Async(string someParameter) { if (string.IsNullOrWhiteSpace(someParameter)) { return Task.FromResult(false); } return _serviceClient.DoSomeExpensiveCheckAsync(someParameter); } } 

我应该做DoSomething1Async还是DoSomething2Async

根据这个答案 ,我不应该用一个不必要的await包装然后我必须使用Task.FromResult(false)进行短路,就像DoSomething2Async

但根据这个答案 ,有些情况下使用try/catchusing语句,我实际应该在返回之前await

我是否正确地说,那

  1. 如果我必须使用try/catch或者using那么我应该await

  2. 否则,如果您只是要返回,请不要await 。 并使用Task.FromResult进行短路

我更喜欢DoSomething1Async ,如果有人说这无关紧要,我想在任何地方都这样做。

如果你担心它,请缓存Task

 static readonly Task falseTask = Task.FromResult(false); 

async关键字还会在返回的Task包含exception,以及正确的堆栈跟踪。 这是权衡行为的安全性。

让我们看看每种情况会有所不同的差异情景:

 async Task UseSomething1Async(string someParameter) { // if IsNullOrWhiteSpace throws an exception, it will be wrapped in // the task and not thrown here. Task t1 = DoSomething1Async(someParameter); // rather, it'll get thrown here. this is best practice, // it's what users of Task-returning methods expect. await t1; // if IsNullOrWhiteSpace throws an exception, it will // be thrown here. users will not expect this. Task t2 = DoSomething2Async(someParameter); // this would never have been reached. await t2; } 

只是在这里说明一点 – IsNullOrWhiteSpace实际上并没有因任何原因抛出任何exception。

就堆栈跟踪而言,异步堆栈跟踪由您await位置决定。 没有await意味着该方法将从堆栈跟踪中消失。

DoSomeExpensiveCheckAsync会抛出exception。 在DoSomething1Async的情况下,堆栈跟踪看起来像caller -> DoSomething1Async -> DoSomeExpensiveCheckAsync

DoSomething2Async的情况下,堆栈跟踪看起来像caller -> DoSomeExpensiveCheckAsync 。 根据代码的复杂程度,这可能会使调试变得困难。

实际上,如果我知道在它之前不会抛出任何exception,并且方法名称仅仅是转发到另一个重载的过载,我通常只会直接返回一个Task 。 这条规则总有例外,你必须要有最大化性能的地方。 只需仔细挑选,即可意识到您可能会让您和您的用户的生活更加艰难。

这并不重要 。 如果您对使用async关键字始终标记Task -returning方法感到满意,那么继续使用DoSomething1

正如你所说,这是一个权衡:

  • DoSomething2不生成async方法所需的状态机,因此它稍微快一点(但差异几乎可以忽略不计)。

  • 另一方面,它可能会对exception处理产生一些无法预料的副作用,因为在async方法中,exception将存储在返回的Task中,而另一方面则会定期抛出exception。