linq lambda在循环中如何工作?
我试图在我的应用程序中实现任务。linq lambda在循环中如何工作?
这里的示例代码:
有一个简单的接口I,3类衍生自它(A,B,C) 创建是列表,以A,B,C的实例poplualte它,然后为对方创建一个任务来调用方法do1();
interface I
{
void do1();
}
class A : I
{
public void do1()
{
Console.WriteLine("A");
}
}
class B : I
{
public void do1()
{
Console.WriteLine("B");
}
}
class C : I
{
public void do1()
{
Console.WriteLine("C");
}
}
class Program
{
public static void Main(string[] args)
{
List<I> l = new List<I>();
l.Add(new A());
l.Add(new B());
l.Add(new C());
var TaskPool = new List<Task>();
foreach (var i in l)
{
Task task = new Task(() => i.do1()
);
TaskPool.Add(task);
}
foreach (var c in TaskPool)
{
c.Start();
}
Thread.Sleep(3000);
Console.Read();
}
}
我期待看到
A
B
C
在输出中,而是它我得到
C
C
C
我还挺发现在调试问题:所有任务具有相同委托,但我不知道为什么以及如何解决此问题。
这是一个很常见的问题。它涉及“捕获变量”如何工作;短版,你需要这样的:
foreach (var i in l)
{
var copy = i;
Task task = new Task(() => copy.do1());
TaskPool.Add(task);
}
这里的问题是,i
(从foreach
)是技术上宣布外循环的范围,因而在该外范围拍摄;您每次捕获相同的变量(C#捕获变量,而不是*值)。在内部添加copy
,循环范围改变了这个;由于范围,copy
分别被捕获每次迭代。
问题是,您在分配代表时捕获循环变量i
。如果您在分配之前创建临时副本,则可以避免捕获变量并获取所需的值。
foreach (var i in l)
{
var local = i;
Task task = new Task(() => local.do1());
TaskPool.Add(task);
}
这是linq表达式的预期行为。这有点被称为变量捕获。有关此主题的一些详细信息,请参阅this link。
就你而言,只需用方法组替换linq表达式即可。我的意思是:这 ...
Task task = new Task(i.do1);
,而不是这个......
Task task = new Task(() => i.do1());
编辑:还有一件非常重要的事情。您已通过添加A,B,C(按特定顺序)将项目添加到列表中。这并不能保证A任务将在B任务之前运行,等等。你可以得到任何东西作为输出,ABC,ACB等等。
+1不是一个很好的解释 - 但解决方案是优雅的,它的工作原理:) – 2010-09-26 10:44:50
根据Marc Gravell♦的评论 – 2010-09-26 10:48:04
,我仍然很难看出差异我提供的链接有关于该主题的一些很好的信息。我认为作家以比我更好的方式解释它。 – Yogesh 2010-09-26 10:49:34
感谢您的解释。 “复制”真的是一个副本吗?据我所知,这只是另一个参考,赖特? – 2010-09-26 10:46:47
@portland:它是'i' *的*值的副本,无论这个值是...无论它是一个引用还是一个值类型值。 – 2010-09-26 11:11:24