来自封闭块的变量在匿名函数中变为空
Variable from enclosing block becomes null in 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);
}));
bytes = null; // EDIT: This last line is what I did wrong. This line
// of code is executed in PARALLEL with the anonymous
// function, and thus this variable is set to null
// sometime while (or before) the lambda is executing.
以上代码在一个线程中执行以避免阻塞UI。
我正在尝试从 Internet 下载图像到 BitmapImage
对象中。图片已正确下载,但当我尝试在我的 UI 中使用它时(使用 Dispatcher.Invoke
),我收到此错误消息:The calling thread cannot access this object because a different thread owns it.
所以我添加了在 UI 线程上创建图像的代码。但是现在,当代码到达 <-- null
指示的行时,变量 bytes
突然变为空。在执行进入匿名函数之前它不是空的。 (我用调试器检查过)
有人知道这是为什么吗? Google 不是很有帮助。
将 bytes
的变量类型更改为 var
没有任何区别。
很可能您稍后要更改 bytes
变量,因此会修改匿名函数内的 "captured" 值。类似于:
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 = ...
是 before bytes = null;
行,它实际上可能会以相反的顺序执行(不确定它在其他线程上运行)。
您应该非常小心这种将异步执行的捕获 later/on 其他线程。更安全的选择是在单独的方法中创建匿名函数,这样您以后就无法更改捕获的变量:
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));
这是由为方法创建的闭包引起的。
它引用了一个超出其范围的对象。 Alexei 的回答将解决问题。
但是如果您仍然不想在代码中保留类似 lambda 的语法,您可以像这样做同样的事情:
var bytes = client.DownloadData(webUrl);
Application.Current.Dispatcher.BeginInvoke((Action<byte[]>)(b =>
{
// draw a pink bunny , or what ever here.
}),bytes);
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);
}));
bytes = null; // EDIT: This last line is what I did wrong. This line
// of code is executed in PARALLEL with the anonymous
// function, and thus this variable is set to null
// sometime while (or before) the lambda is executing.
以上代码在一个线程中执行以避免阻塞UI。
我正在尝试从 Internet 下载图像到 BitmapImage
对象中。图片已正确下载,但当我尝试在我的 UI 中使用它时(使用 Dispatcher.Invoke
),我收到此错误消息:The calling thread cannot access this object because a different thread owns it.
所以我添加了在 UI 线程上创建图像的代码。但是现在,当代码到达 <-- null
指示的行时,变量 bytes
突然变为空。在执行进入匿名函数之前它不是空的。 (我用调试器检查过)
有人知道这是为什么吗? Google 不是很有帮助。
将 bytes
的变量类型更改为 var
没有任何区别。
很可能您稍后要更改 bytes
变量,因此会修改匿名函数内的 "captured" 值。类似于:
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 = ...
是 before bytes = null;
行,它实际上可能会以相反的顺序执行(不确定它在其他线程上运行)。
您应该非常小心这种将异步执行的捕获 later/on 其他线程。更安全的选择是在单独的方法中创建匿名函数,这样您以后就无法更改捕获的变量:
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));
这是由为方法创建的闭包引起的。 它引用了一个超出其范围的对象。 Alexei 的回答将解决问题。
但是如果您仍然不想在代码中保留类似 lambda 的语法,您可以像这样做同样的事情:
var bytes = client.DownloadData(webUrl);
Application.Current.Dispatcher.BeginInvoke((Action<byte[]>)(b =>
{
// draw a pink bunny , or what ever here.
}),bytes);