事件触发前的对象处理和垃圾收集

一段代码由我正在与之交谈的人提出:

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结束,在WebClient上调用了Dispose() 。 在DownloadStringAsync()完成和DownloadStringCompleted事件触发之前,是否有任何东西会阻止WebClientusing甚至垃圾收集掉?

有一个更新的方法, 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的生命周期,还是这是一个糟糕的代码设计?

WebClient没有实现IDisposable,它的基类是Component。

Component类释放使用其Events属性注册的任何事件处理程序,但WebClient不使用该属性。

在WebClient上调用Dispose对webclient管理的任何状态都没有影响。

内部资源的实际处理是在私有方法DownloadBits和内部类DownloadBitsState

因此,您显示的代码现在由于过早释放资源而没有任何影响。 然而,这是由实现细节引起的。 那些可能会在未来发生变化。

由于框架跟踪待处理的回调,您不必担心过早的垃圾收集,正如本回答中所解释的那样,由Alexei Levenkov友情提供。

该事件仅用于DownloadStringAsync ,而不用于DownloadStringTaskAsync

对于后者,任务是Task ,当任务完成时,它包含下载的响应。

因此,第二个例子可以改写为:

 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 } } 

你完全正确,第一个例子是可怕的破坏。 在大多数情况下,我希望在异步任务完成之前处理客户端对象,甚至可能在您提及时进行垃圾收集。 丢失事件后的第二个示例没有这些问题,因为它将在处置客户端对象之前正确等待下载完成。