好吧,我这里有点问题。这是循环。
lock (ClientLocker)
{
Trace.WriteLine("#WriteAll: " + sm.Header);
foreach (Client c in Clients)
{
if (c.LoggedIn)
{
Trace.WriteLine("#TryWriteTo[" + c.Id + "](" + sm.Header + ")");
LazyAsync.Invoke(() => c.WriteMessage(sm));
}
}
}
这是LazySync
public static class LazyAsync
{
public static void Invoke(Action a)
{
a.BeginInvoke(a.EndInvoke, null);
}
}
每个Client
都包含一个socket
,所以我几乎不能Clone
它。问题是,当我执行Invoke
到c.WriteMessage
时,由于执行被延迟,它通常不会在列表中的第一对上激发,有时实际上只会在最后一项上激发一整串。
我知道这与c是一个在实际调用Invoke
之前发生更改的引用有关,但有办法避免这种情况吗?
执行一般的for(int i=0 etc
循环似乎并不能解决这个问题。
有人知道我该怎么解决这个问题吗?
记住,不能Clone
Client
。
将c
复制到本地变量,如下所示:
lock (ClientLocker)
{
Trace.WriteLine("#WriteAll: " + sm.Header);
foreach (Client c in Clients)
{
if (c.LoggedIn)
{
Client localC = c;
Trace.WriteLine("#TryWriteTo[" + c.Id + "](" + sm.Header + ")");
LazyAsync.Invoke(() => localC.WriteMessage(sm));
}
}
}
如果你想获得更多信息,请在网上搜索:"访问修改过的闭包"。
您的怀疑是正确的:变量c
由lambda表达式捕获,但要稍后才能求值。
每当您在lambda表达式中使用循环变量时,就会弹出这种类型的错误,因为循环变量的作用域在循环之外,而不是在循环的每次迭代中。
您可以通过在foreach
循环中创建一个新的局部变量来解决此问题,将c
分配给它,然后将该新的本地变量传递到lambda表达式中:
lock (ClientLocker)
{
Trace.WriteLine("#WriteAll: " + sm.Header);
foreach (Client c in Clients)
{
if (c.LoggedIn)
{
Trace.WriteLine("#TryWriteTo[" + c.Id + "](" + sm.Header + ")");
Client copyOfC = c;
LazyAsync.Invoke(() => copyOfC.WriteMessage(sm));
}
}
}
以下是一些相关的StackOverflow帖子:
- 访问修改后的闭包
- C#重用foreach中的变量有什么原因吗
- C#lambda,局部变量值在你想的时候没有取
尝试将c
设置为局部变量,并在此基础上调用LazyAsync.Invoke
,以避免c
在调用发生之前被foreach
循环重新分配。当LazyAsync.Invoke
执行c.WriteMessage
时,它调用的是c
现在指向的任何WriteMessage
,而不是评估LazyAsync.Invoke(() => c.WriteMessage(sm))
时的
foreach (Client c in Clients)
{
if (c.LoggedIn)
{
Trace.WriteLine("#TryWriteTo[" + c.Id + "](" + sm.Header + ")");
Client client = c;
LazyAsync.Invoke(() => client.WriteMessage(sm));
}
}