If I have:
var myObjects = new ConcurrentBag<object>();
And try to remove objects via:
foreach (var myObject in myObjects.ToArray())
{
myObjects.TryTake(out myObject);
}
The compiler complains with: "Readonly local variable cannot be used as an assignment target"
Yet, if I add a local reference within the foreach it compiles:
foreach (var myObject in myObjects.ToArray())
{
var localReference = myObject;
myObjects.TryTake(out localReference);
}
What exactly is going on here?
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:
The type and identifier of a foreach statement declare the iteration variable of the statement.
The iteration variable corresponds to a read-only local variable with a scope that extends over the embedded statement.
During execution of a foreach statement, the iteration variable represents the collection element for which an iteration is currently being performed.
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.
If you want to assign the retrieved object to the array, use a for-statement instead, since you cannot assign a value to the loop variable
object[] arr = myObjects.ToArray();
for (int i = 0; i < arr.Length; i++) {
myObjects.TryTake(out arr[i]);
}
Now the array contains the retrieved objects or null where no object could be taken.
After having read about ConcurrentBag<T>
, I think I am beginning to understand what you have in mind. My first solution above runs into race conditions in multithreaded scenarios (the very and only reson for using a ConcurrentBag<T>
). Try this instead
var removedObjects = new List<object>();
object obj;
while (myObjects.TryTake(out obj)) {
removedObjects.Add(obj);
}
Note: Assigning the objects of the bag to an array and then looping through it is not a good idea, since other threads could have added or removed objects meanwhile. Therefore you must use this direct approach.
If you only want to remove the objects without retrieving them at the same time:
object obj;
while (myObjects.TryTake(out obj)) {
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With