3

これは答えられるべきだと思われますが、私が見つけた潜在的な重複は別のことを求めていました...

私はこれがうまくいくように見えることに気づきました(sourceDirInclusion単純ですDictionary<X,Y>

    foreach (string dir in sourceDirInclusion.Keys)
    {
        if (sourceDirInclusion[dir] == null)
            sourceDirInclusion.Remove(dir);
    }

それは、コレクションからアイテムを削除するのforeachが安全であることを意味しますか、それとも私が幸運に恵まれたことを意味しますか?

削除するのではなく、辞書に要素を追加した場合はどうなりますか?

私が解決しようとしている問題は、sourceDirInclusion最初にデータが入力されることですが、その後、各値が2回目のパスで辞書に新しいアイテムを提供する可能性があります。たとえば、私がやりたいことは次のようなものです。

foreach (string dir in sourceDirInclusion.Keys)
{
  X x = sourceDirInclusion[dir];
  sourceDirInclusion.Add(X.dir,X.val);
}
4

4 に答える 4

7

簡単な答え:これは安全ではありません。

長い答え:IEnumerator<T>ドキュメントから:

コレクションが変更されない限り、列挙子は有効なままです。要素の追加、変更、削除など、コレクションに変更が加えられた場合、列挙子は回復不能に無効になり、その動作は未定義になります。

ドキュメントには、動作が未定義であると記載されていることに注意してください。つまり、動作する可能性があり、動作しない可能性があります。未定義の振る舞いに頼ってはいけません。

Keysこの場合、列挙を開始するときにキーのリストのコピーを作成するかどうかに関して、列挙可能なものの動作に依存します。この特定のケースでは、ドキュメントから、からの戻り値Dictionary<,>.Keysがディクショナリを参照するコレクションであることがわかります。

返さDictionary<TKey, TValue>.KeyCollectionれるのは静的コピーではありません。代わりに、Dictionary<TKey, TValue>.KeyCollectionは元のキーを参照しますDictionary<TKey, TValue>。したがって、への変更はDictionary<TKey, TValue>引き続きに反映されますDictionary<TKey, TValue>.KeyCollection

したがって、辞書のキーを列挙しているときに辞書を変更することは安全ではないと見なす必要があります。

これは1回の変更で修正できます。この行を変更します。

foreach (string dir in sourceDirInclusion.Keys)

これに:

foreach (string dir in sourceDirInclusion.Keys.ToList())

拡張メソッドは、ToList()キーのリストの明示的なコピーを作成し、辞書を安全に変更できるようにします。「基になるコレクション」はコピーであり、オリジナルではありません。

于 2013-01-24T17:40:58.687 に答える
3

投げるなら

InvalidOperationException:Message="コレクションが変更されました;列挙操作が実行されない可能性があります

これを回避するには、削除の候補を外部リストに追加します。次に、それをループして、ターゲットコンテナ(辞書)から削除します。

List<string> list = new List<string>(sourceDirInclusion.Keys.Count);
foreach (string dir in sourceDirInclusion.Keys)
{
    if (sourceDirInclusion[dir] == null)
        list.Add(dir);
}
foreach (string dir in list)
{
    sourceDirInclusion.Remove(dir);
}
于 2013-01-24T17:40:37.097 に答える
0

これをチェックしてください:「foreach」ループでリストを変更する最良の方法は何ですか?

要するに:

The collection used in foreach is immutable. This is very much by design.

それがMSDNで言うように:

foreachステートメントは、コレクションを反復処理して必要な情報を取得するために使用されますが、予期しない副作用を回避するために、ソースコレクションからアイテムを追加または削除するために使用することはできません。ソースコレクションにアイテムを追加または削除する必要がある場合は、forループを使用します。

更新: 代わりにforループを使用できます:

for (int index = 0; index < dictionary.Count; index++) {
  var item = dictionary.ElementAt(index);
  var itemKey = item.Key;
  var itemValue = item.Value;
}
于 2013-01-24T17:40:44.577 に答える
0

これは、sourceDirInclusion.Keysをトラバースしているために機能します。

ただし、FrameWorkの将来のバージョンで確認するために、foreachステートメントでsourceDirInclusion.Keys.ToArray()を使用することをお勧めします。この方法で、ループするキーのコピーを作成します。

ただし、これは機能しません。

foreach(KeyValuePair<string, object> item in sourceDirInclusion)
{
    if (item.Value == null)
        sourceDirInclusion.Remove(item.Key);
}

原則として、トラバース中にコレクションを変更することはできませんが、多くの場合、.ToArray()または.ToList()を使用して新しいコレクションを作成し、元のコレクションを変更しながらそれをトラバースできます。

あなたの探求で頑張ってください。

于 2013-01-24T17:44:38.940 に答える