我正在与之交谈的人提出了一段代码:
private void DownloadInformation(string id)
{
using (WebClient wc = new WebClient())
{
wc.DownloadStringCompleted +=
new DownloadStringCompletedEventHandler(DownloadStringCompleted);
wc.DownloadStringAsync(new Uri("http://www.fake.com/" + id));
}
}
以上是此简化版:
(我已获得作者发布图片的权限。)
关于该代码的困扰在于附加了事件处理程序,DownloadStringAsync()
被调用,然后using
块结束,在Dispose()
上调用WebClient
。是否有任何措施可以阻止WebClient
被using
处理掉,甚至在DownloadStringAsync()
完成DownloadStringCompleted
事件触发之前收集垃圾?
我有一种较新的方法DownloadStringTaskAsync()
,我认为它与await
一起使用:
private async Task DownloadInformation(string id)
{
using (WebClient wc = new WebClient())
{
wc.DownloadStringCompleted += DownloadStringCompleted;
await wc.DownloadStringTaskAsync(new Uri("http://www.fake.com/" + id));
}
}
然而,即便如此......我基本上会认为在WebClient
被处置掉之前会调用事件触发器和处理程序。
我是否误解了WebClient
在这种情况下的生命周期,或者这是一个糟糕的代码设计?
答案 0 :(得分:4)
WebClient没有实现IDisposable,它的基类是Component。
Component类释放任何使用Events属性注册的事件处理程序,但WebClient不使用该属性。
在WebClient上调用Dispose对webclient管理的任何状态都没有影响。
内部资源的实际处理是在私有方法DownloadBits
和内部类DownloadBitsState
中完成的。
因此,由于过早释放资源,您显示的代码现在没有任何影响。然而,这是由实现细节引起的。这些可能在未来发生变化。
由于框架跟踪待处理的回调,您不必担心过早的垃圾收集,如this answer中所述,由Alexei Levenkov友情提供。
答案 1 :(得分:3)
该事件仅与DownloadStringAsync
一起使用,而不与DownloadStringTaskAsync
一起使用。
使用后者,任务为Task<string>
,当任务完成时,它包含下载的响应。
因此第二个例子可以改写为:
private async Task DownloadInformation(string id)
{
using (WebClient wc = new WebClient())
{
string response = await wc.DownloadStringTaskAsync(new Uri("http://www.fake.com/" + id));
// TODO: Process response
}
}
你完全正确,因为第一个例子非常糟糕。在大多数情况下,我希望在异步任务完成之前处理客户端对象,甚至可能在您提及时进行垃圾收集。丢失事件后的第二个示例没有任何问题,因为它会在处置客户端对象之前正确等待下载完成。