事件触发前的对象处理和垃圾收集
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
事件触发之前,有什么可以阻止 WebClient
被 using
处理掉甚至垃圾收集吗?
有一个更新的方法,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 友情提供。
与我交谈的人提出了一段代码:
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
事件触发之前,有什么可以阻止 WebClient
被 using
处理掉甚至垃圾收集吗?
有一个更新的方法,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 友情提供。