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

Object disposal and garbage collection prior to event triggering

与我交谈的人提出了一段代码:

private void DownloadInformation(string id)
{
    using (WebClient wc = new WebClient())
    {
        wc.DownloadStringCompleted += 
            new DownloadStringCompletedEventHandler(DownloadStringCompleted);
        wc.DownloadStringAsync(new Uri("http://www.fake.com/" + id));
    }
}

以上是这个的简化版本:

(本人已获得作者许可 post 图片。)

让我困扰的是该代码附加了一个事件处理程序,调用 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 的生命周期,或者这是一个糟糕的代码设计?

该事件仅用于 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
    }
}

您完全正确,第一个示例严重损坏。在大多数情况下,我希望客户端对象在异步任务完成之前被处理掉,它甚至可能像您提到的那样被垃圾收集。第二个示例在事件丢失后出现了 none 个问题,因为它会在处理客户端对象之前正确地等待下载完成。

WebClient 没有实现 IDisposable,它的基础 class 组件实现了。

组件 class 处理在其事件中注册的任何事件处理程序 属性 但 WebClient 不使用该 属性。

在 WebClient 上调用 Dispose 不会影响 Webclient 管理的任何状态。

内部资源的实际处理是在私有方法 DownloadBits 和内部 class DownloadBitsState 中完成的。

所以你展示的代码由于释放资源过早,到目前为止没有任何影响。然而,这是由实现细节引起的。那些将来可能会改变。

由于框架跟踪挂起的回调,您也不必担心过早的垃圾收集,如 this answer 中所述,由 Alexei Levenkov 友情提供。