async 是一个关键字,用于将方法标记为编译器完全重写以支持 async/await 模式的候选方法。
发生的情况是,该方法在您在内部使用 await 关键字的每个点都被拆分。如果您将方法标记为async 但不使用await,它将抱怨您将其标记为不必要,或者您忘记使用await。所以这就是你得到编译器错误的原因。
现在,下一步是将await 添加到您的方法中,但这意味着稍微更改它:
- 为了运行一项任务而启动另一项任务太过分了
- 在任务上使用
.Result 很容易出现死锁,这就是await 派上用场的地方
所以让我们重写你的方法。首先我们去掉对Task.Result 的调用并替换为await:
protected async Task<T> PerformGet<T>(string requestUri, bool appendHeader)
{
try
{
_webApiClient = new HttpClient { BaseAddress = _baseAddress };
if (appendHeader) AppendDefaultHeaders();
var webApiResponse = await Task.Run(() => _webApiClient.GetAsync(requestUri));
var responseContent = await webApiResponse.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(responseContent);
}
catch (Exception ex) {
throw ex;
}
}
然后我们去掉不必要的task-in-task:
protected async Task<T> PerformGet<T>(string requestUri, bool appendHeader)
{
try
{
_webApiClient = new HttpClient { BaseAddress = _baseAddress };
if (appendHeader) AppendDefaultHeaders();
var webApiResponse = await _webApiClient.GetAsync(requestUri);
var responseContent = await webApiResponse.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(responseContent);
}
catch (Exception ex) {
throw ex;
}
}
然后我们去掉不必要的try/catch:
protected async Task<T> PerformGet<T>(string requestUri, bool appendHeader)
{
_webApiClient = new HttpClient { BaseAddress = _baseAddress };
if (appendHeader) AppendDefaultHeaders();
var webApiResponse = await _webApiClient.GetAsync(requestUri);
var responseContent = await webApiResponse.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(responseContent);
}
然后我们将HttpClient 对象放入using 语句中(参见下面的注释):
protected async Task<T> PerformGet<T>(string requestUri, bool appendHeader)
{
using (var client = new HttpClient { BaseAddress = _baseAddress })
{
if (appendHeader) AppendDefaultHeaders(client);
var response = await client.GetAsync(requestUri);
var responseContent = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(responseContent);
}
}
这删除了字段,这在进行异步工作时通常是一个坏主意,并使其成为此方法的本地概念。这也需要更改 AppendDefaultHeaders 方法以接受客户端应将标头作为参数添加到其中。
注意关于HttpClient。虽然上面的代码把它放在using 子句中,这是因为原始代码构造了一个实例,而HttpClient 实现了IDisposable。
但是,您不应该这样做,而应该重用 HttpClient 实例。您可以在这里阅读更多相关信息,You're using HttpClient wrong and it's destabilizing your software