9

如果我有:

var myObjects = new ConcurrentBag<object>();

并尝试通过以下方式删除对象:

foreach (var myObject in myObjects.ToArray())
{
    myObjects.TryTake(out myObject);
}

编译器抱怨:“只读局部变量不能用作赋值目标”

但是,如果我在 foreach 中添加本地引用,它会编译:

foreach (var myObject in myObjects.ToArray())
{
    var localReference = myObject;
    myObjects.TryTake(out localReference);
}

这里到底发生了什么?

4

2 回答 2

16

The iteration variable in a foreach (i.e. myObject) cannot be assigned a new value inside the foreach. It is not allowed.

In the first scenario, the out tries to do this. In the second scenario, you *never try to reassign to myObject, so it is fine.

To quote from the ECMA specification, 15.8.4, emphasis mine:

  1. The type and identifier of a foreach statement declare the iteration variable of the statement.

  2. The iteration variable corresponds to a read-only local variable with a scope that extends over the embedded statement.

  3. During execution of a foreach statement, the iteration variable represents the collection element for which an iteration is currently being performed.

  4. A compile-time error occurs if the embedded statement attempts to modify the iteration variable (via assignment or the ++ and --operators) or pass the iteration variable as a ref or out parameter.

于 2012-06-28T19:52:09.100 回答
6

如果要将检索到的对象分配给数组,请改用 for 语句,因为您无法为循环变量分配值

object[] arr = myObjects.ToArray();
for (int i = 0; i < arr.Length; i++) {
    myObjects.TryTake(out arr[i]); 
}

现在数组包含检索到的对象或 null ,其中没有对象可以被获取。


更新

读完之后ConcurrentBag<T>,我想我开始明白你的想法了。我上面的第一个解决方案在多线程场景中遇到了竞争条件(使用 a 的唯一原因ConcurrentBag<T>)。试试这个

var removedObjects = new List<object>();
object obj;
while (myObjects.TryTake(out obj)) {
    removedObjects.Add(obj);
}

注意:将包中的对象分配给一个数组然后循环遍历它不是一个好主意,因为其他线程可能同时添加或删除了对象。因此,您必须使用这种直接方法。


如果您只想删除对象而不同时检索它们:

object obj;
while (myObjects.TryTake(out obj)) {
}
于 2012-06-28T20:08:00.610 回答