封闭块中的变量在匿名函数中变为null

时间:2015-05-23 18:03:50

标签: c# wpf multithreading null anonymous-function

var client = new WebClient();
var bytes = client.DownloadData(webUrl); <-- NOT null

Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
    BitmapImage img = new BitmapImage();
    img.BeginInit();
    img.StreamSource = new MemoryStream(bytes); <-- null
    img.EndInit();
    img_DownloadCompleted(img, webUrl);
}));

以上代码在线程中执行,以避免阻止UI。

我正在尝试将图像从互联网下载到BitmapImage对象中。图像已正确下载,但当我尝试在我的UI中使用它时(使用Dispatcher.Invoke),我收到以下错误消息:The calling thread cannot access this object because a different thread owns it.

所以我添加了在UI线程上创建图像的代码。但现在,当代码到达<-- null指示的行时,变量bytes突然变为空。在执行进入匿名函数之前它不为null。 (我检查过调试器)

有谁知道这是为什么?谷歌不是很有帮助。

bytes的变量类型更改为var并没有任何区别。

2 个答案:

答案 0 :(得分:5)

您很可能稍后更改bytes变量,从而修改匿名函数内的“捕获”值。类似的东西:

var bytes = client.DownloadData(webUrl); <-- NOT null
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
 ...  img.StreamSource = new MemoryStream(bytes); <-- null
 ...
}
bytes = null; // something like this - because why not? 

请注意,即使代码看起来像顺序而且img.StreamSource = ...bytes = null;行之前是,它实际上可能会以相反的顺序执行(在其他线程上运行时不确定) )。

您应该非常小心这些捕获将在以后/在其他线程上异步执行。更安全的选项是在单独的方法中创建匿名函数,以便您以后不能更改捕获的变量:

Action CreateBitmapAction(bytes[] bytes)
{
 return () =>
 {
    BitmapImage img = new BitmapImage();
    img.BeginInit();
    img.StreamSource = new MemoryStream(bytes);
    img.EndInit();
    img_DownloadCompleted(img, webUrl);
 };
}

Application.Current.Dispatcher.BeginInvoke(CreateBitmapAction(bytes));

答案 1 :(得分:1)

这是由为该方法创建的闭包引起的。 它引用了一个超出它范围的对象。阿列克谢的回答将解决这个问题。

但如果您仍然不想在代码中保留类似lambda的语法,那么就可以这样做:

   var bytes = client.DownloadData(webUrl);
   Application.Current.Dispatcher.BeginInvoke((Action<byte[]>)(b => 
   {
       // draw a pink bunny , or what ever here.   
   }),bytes);