将WebClient方法转换为async/await

45

我有一些现有的代码,正在将其迁移到Windows 8 WinRT平台。该代码从URL获取数据,并异步调用传递的委托:

private void RequestData(string uri, Action<string> action)
{
  var client = new WebClient();
  client.DownloadStringCompleted += (s,e) => action(e.Result);
  client.DownloadStringAsync(new Uri(uri));
}

转换为WinRT需要使用 HttpClient 和异步方法。我已经阅读了一些关于异步/等待的教程,但有点困惑。我如何更改上面的方法,但保持方法签名以避免改变我的代码更多?

6个回答

101

1
唯一一个显示整个方法并保持相同签名的答案,因此是唯一回答问题的答案。 :) - James Manning
7
简短说明:除非你使用“发出请求并忘记”的方式,否则请将返回类型更改为Task。否则,调用线程会继续执行,无论你是否等待RequestData的完成。 - João Nogueira
5
使用私有的异步任务(private async Task)更好,而不是使用异步无返回值(async void)。 - Tony
3
重要的是不要错过这里调用的不同方法: DownloadString*TASK*AsyncDownloadStringAsync。由于需要保持向后兼容性,因此需要在类中添加一个新的方法。 - Simon_Weaver
1
@KeyBored 根据您的具体用例,您可以尝试这里的其中一种方法(https://dotnetfiddle.net/vHkRay)。 - Cole Cameron
显示剩余5条评论

11

如何更改上述方法,但保留方法签名以避免更改更多的代码?

最好的答案是“不要这样做”。如果你使用了async,那么就要一路使用下去。

private async Task<string> RequestData(string uri)
{
  using (var client = new HttpClient())
  {
    return await client.GetStringAsync(uri);
  }
}

方法RequestData缺少async修饰符? :) - James Manning
就保持“传递回调”签名而言,考尔的答案在我看来似乎是合理的 - 它仍然是异步的,使用了await,并在“正确”的线程上调用操作(假设有同步上下文)。据我所知? 我自由地承认,像这样传递回调并不像具有“看起来同步”的“正常”异步/等待代码那样好,但如果您有现有的接受回调的代码,则仍然接受它们似乎是合理的。当然,“异步void”通常是个坏主意,所以也许我忘记了某些问题情况。 :) - James Manning
谢谢Stephen,但我正在迁移代码,因此尽量减少更改是我的首要任务。这就是为什么我想保留给定的签名。 - ColinE
2
async void 存在一些问题。例如单元测试。但最适用于此情况的是它如何处理错误:任何与 HTTP 下载相关的问题都会直接在 SynchronizationContext 上引发异常,而 async Task 允许更自然地处理异常(从 await 引发)。 - Stephen Cleary

8

按照这个例子,首先创建异步任务,然后使用await获取其结果:

Task<string> downloadStringTask = client.DownloadStringTaskAsync(new Uri(uri));
string result = await downloadStringTask;

5
var client = new WebClient();
string page = await client.DownloadStringTaskAsync("url");

或者
var client = new HttpClient();
string page = await client.GetStringAsync("url");

3

-1

这段代码是用于UploadValuesAsync的:

public class WebClientAdvanced : WebClient
{
    public async Task<byte[]> UploadValuesAsync(string address, string method, IDictionary<string, string> data)
    {
        var nvc = new NameValueCollection();
        foreach (var x in data) nvc.Add(x.Key, x.Value.ToStr());

        var tcs = new TaskCompletionSource<byte[]>();
        UploadValuesCompleted += (s, e) =>
        {
            if (e.Cancelled) tcs.SetCanceled();
            else if (e.Error != null) tcs.SetException(e.Error);
            else tcs.SetResult(e.Result);
        };

        UploadValuesAsync(new Uri(address), method, nvc);
        var result = await tcs.Task;
        return result;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接