When I run the below code it is supposed fill the ListBox
with each DataRow
's values, but it is filling the ListBox
with single DataRow
's value.
当我运行下面的代码时,它应该用每个DataRow的值填充列表框,但是它是用单个DataRow的值填充列表框。
What is the problem and how can I solve it? This is a C# 4.0 WPF application.
问题是什么,我如何解决它?这是一个c# 4.0 WPF应用程序。
Task.Factory.StartNew(() =>
{
myThread();
});
void myThread()
{
using (DataTable dtTemp = DbConnection.db_Select_DataTable(srQuery))
{
foreach (DataRow drw in dtTemp.Rows)
{
this.Dispatcher.BeginInvoke(new Action(delegate()
{
listBox1.Items.Add(drw["Value"].ToString());
}));
}
}
}
2 个解决方案
#1
6
The problem is that you're capturing the drw
variable in your anonymous method. That variable is being updated in the foreach
loop, so by the time your delegates are invoked on the dispatcher thread, you're getting the same value each time. In C# 5, this would be okay (it's such a common error that the language has changed to avoid it) but before C# 5 you need to copy the variable in the loop:
问题是您正在匿名方法中捕获drw变量。该变量在foreach循环中被更新,因此在dispatcher线程上调用委托时,每次都得到相同的值。在c# 5中,这是可以的(这是一个常见的错误,语言已经改变以避免它),但是在c# 5之前,您需要在循环中复制变量:
foreach (DataRow drw in dtTemp.Rows)
{
DataRow copy = drw;
this.Dispatcher.BeginInvoke(new Action(delegate()
{
listBox1.Items.Add(copy["Value"].ToString());
}));
}
Or even better, do all the data access in the background thread:
或者更好的是,在后台线程中进行所有的数据访问:
foreach (DataRow drw in dtTemp.Rows)
{
string item = drw["Value"].ToString();
this.Dispatcher.BeginInvoke(new Action(delegate()
{
listBox1.Items.Add(item);
}));
}
Note that the change to C# 5 only affects foreach
- not for
loops.
注意,c# 5的更改只影响foreach—而不是for循环。
Also note that your code can be slightly shorted using a lambda expression instead of an anonymous method:
还请注意,您的代码可以使用lambda表达式而不是匿名方法稍微缩短:
foreach (DataRow drw in dtTemp.Rows)
{
string item = drw["Value"].ToString();
Action action = () => listBox1.Items.Add(item);
this.Dispatcher.BeginInvoke(action);
}
#2
4
Variable capturing strikes again. (Google: variable capture)
变量捕获再次罢工。(谷歌:变量捕获)
Try creating a temporary variable and passing that to the thread, like this:
尝试创建一个临时变量并将其传递给线程,如下所示:
void myThread()
{
using (DataTable dtTemp = DbConnection.db_Select_DataTable(srQuery))
{
foreach (DataRow drw in dtTemp.Rows)
{
DataRow tmp = drw;
this.Dispatcher.BeginInvoke(new Action(delegate()
{
listBox1.Items.Add(tmp["Value"].ToString());
}));
}
}
}
#1
6
The problem is that you're capturing the drw
variable in your anonymous method. That variable is being updated in the foreach
loop, so by the time your delegates are invoked on the dispatcher thread, you're getting the same value each time. In C# 5, this would be okay (it's such a common error that the language has changed to avoid it) but before C# 5 you need to copy the variable in the loop:
问题是您正在匿名方法中捕获drw变量。该变量在foreach循环中被更新,因此在dispatcher线程上调用委托时,每次都得到相同的值。在c# 5中,这是可以的(这是一个常见的错误,语言已经改变以避免它),但是在c# 5之前,您需要在循环中复制变量:
foreach (DataRow drw in dtTemp.Rows)
{
DataRow copy = drw;
this.Dispatcher.BeginInvoke(new Action(delegate()
{
listBox1.Items.Add(copy["Value"].ToString());
}));
}
Or even better, do all the data access in the background thread:
或者更好的是,在后台线程中进行所有的数据访问:
foreach (DataRow drw in dtTemp.Rows)
{
string item = drw["Value"].ToString();
this.Dispatcher.BeginInvoke(new Action(delegate()
{
listBox1.Items.Add(item);
}));
}
Note that the change to C# 5 only affects foreach
- not for
loops.
注意,c# 5的更改只影响foreach—而不是for循环。
Also note that your code can be slightly shorted using a lambda expression instead of an anonymous method:
还请注意,您的代码可以使用lambda表达式而不是匿名方法稍微缩短:
foreach (DataRow drw in dtTemp.Rows)
{
string item = drw["Value"].ToString();
Action action = () => listBox1.Items.Add(item);
this.Dispatcher.BeginInvoke(action);
}
#2
4
Variable capturing strikes again. (Google: variable capture)
变量捕获再次罢工。(谷歌:变量捕获)
Try creating a temporary variable and passing that to the thread, like this:
尝试创建一个临时变量并将其传递给线程,如下所示:
void myThread()
{
using (DataTable dtTemp = DbConnection.db_Select_DataTable(srQuery))
{
foreach (DataRow drw in dtTemp.Rows)
{
DataRow tmp = drw;
this.Dispatcher.BeginInvoke(new Action(delegate()
{
listBox1.Items.Add(tmp["Value"].ToString());
}));
}
}
}