如果我有:
var myObjects = new ConcurrentBag<object>();
尝试通过以下方式删除对象:
foreach (var myObject in myObjects.ToArray())
{
myObjects.TryTake(out myObject);
}
编译器抱怨:&#34; Readonly局部变量不能用作分配目标&#34;
然而,如果我在foreach中添加一个本地引用,它会编译:
foreach (var myObject in myObjects.ToArray())
{
var localReference = myObject;
myObjects.TryTake(out localReference);
}
这到底发生了什么?
答案 0 :(得分:16)
foreach
(即myObject
)中的迭代变量无法在foreach
内分配新值。这是不允许的。
在第一个场景中,out
尝试执行此操作。在第二种情况下,您*永远不会尝试重新分配给myObject
,所以没关系。
引用ECMA规范15.8.4,强调我的:
foreach语句的类型和标识符声明了 声明的迭代变量。
迭代变量对应于只读本地 变量,其范围扩展到嵌入语句。
在执行foreach语句期间,迭代变量 表示迭代所针对的集合元素 目前正在进行中。
- 醇>
如果嵌入语句尝试,则发生编译时错误 修改迭代变量(通过赋值或++和 - 运算符)或将迭代变量作为ref或out参数传递。
答案 1 :(得分:6)
如果要将检索到的对象分配给数组,请使用for语句,因为您无法为循环变量赋值
object[] arr = myObjects.ToArray();
for (int i = 0; i < arr.Length; i++) {
myObjects.TryTake(out arr[i]);
}
现在数组包含检索到的对象,或者在没有对象的情况下为null。
在阅读ConcurrentBag<T>
之后,我想我开始明白你的想法了。我上面的第一个解决方案遇到了多线程场景中的竞争条件(使用ConcurrentBag<T>
的唯一和唯一的共振)。试试这个
var removedObjects = new List<object>();
object obj;
while (myObjects.TryTake(out obj)) {
removedObjects.Add(obj);
}
注意:将数据包的对象分配给数组然后循环遍历它并不是一个好主意,因为其他线程可能同时添加或删除了对象。因此,您必须使用这种直接方法。
如果您只想删除对象而不同时检索它们:
object obj;
while (myObjects.TryTake(out obj)) {
}