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/catch
和using
语句,我实际应该在返回之前await
。
我是否正确地说,那
-
如果我必须使用
try/catch
或者using
那么我应该await
-
否则,如果您只是要返回,请不要
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。