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

时间:2016-09-21 06:39:31

标签: c# .net garbage-collection event-handling using-statement

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

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

以上是此简化版:

enter image description here

(我已获得作者发布图片的权限。)

关于该代码的困扰在于附加了事件处理程序,DownloadStringAsync()被调用,然后using块结束,在Dispose()上调用WebClient。是否有任何措施可以阻止WebClientusing处理掉,甚至在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在这种情况下的生命周期,或者这是一个糟糕的代码设计?

2 个答案:

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

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