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

foreach(すなわちmyObject)の反復変数には、内の新しい値を割り当てることはできませんforeach。それは許可されていません。

最初のシナリオでは、outこれを実行しようとします。2番目のシナリオでは、*再割り当てを試みないためmyObject、問題ありません。

ECMA仕様15.8.4から引用するには、次のことを強調します。

  1. foreachステートメントのタイプと識別子は、ステートメントの反復変数を宣言します。

  2. 反復変数は、埋め込みステートメントにまたがるスコープを持つ読み取り専用のローカル変数に対応します。

  3. foreachステートメントの実行中、反復変数は、現在反復が実行されているコレクション要素を表します。

  4. 埋め込みステートメントが反復変数を(代入または++および--operatorsを介して)変更しようとした場合、または反復変数をrefまたはoutパラメーターとして渡そうとした場合、コンパイル時エラーが発生します。

于 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>、私はあなたが何を考えているのか理解し始めていると思います。上記の私の最初の解決策は、マルチスレッドシナリオでの競合状態に遭遇します(を使用するための非常に唯一の理由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 に答える