ループでC#ハッシュテーブルを更新する方法は?
-
11-07-2019 - |
質問
ループ内でハッシュテーブルを更新しようとしていますが、エラーが発生しています:System.InvalidOperationException:Collection was modified;列挙操作は実行されません。
private Hashtable htSettings_m = new Hashtable();
htSettings_m.Add("SizeWidth", "728");
htSettings_m.Add("SizeHeight", "450");
string sKey = "";
string sValue = "";
foreach (DictionaryEntry deEntry in htSettings_m)
{
// Get value from Registry and assign to sValue.
// ...
// Change value in hashtable.
sKey = deEntry.Key.ToString();
htSettings_m[sKey] = sValue;
}
それを回避する方法はありますか、それともそのような目的のためのより良いデータ構造がありますか?
解決
最初に別のIEnumerableインスタンスにキーのコレクションを読み込み、次にそのリストでforeachを実行できます
System.Collections.Hashtable ht = new System.Collections.Hashtable();
ht.Add("test1", "test2");
ht.Add("test3", "test4");
List<string> keys = new List<string>();
foreach (System.Collections.DictionaryEntry de in ht)
keys.Add(de.Key.ToString());
foreach(string key in keys)
{
ht[key] = DateTime.Now;
Console.WriteLine(ht[key]);
}
他のヒント
概念的には次のようにします:
Hashtable table = new Hashtable(); // ps, I would prefer the generic dictionary..
Hashtable updates = new Hashtable();
foreach (DictionaryEntry entry in table)
{
// logic if something needs to change or nog
if (needsUpdate)
{
updates.Add(key, newValue);
}
}
// now do the actual update
foreach (DictionaryEntry upd in updates)
{
table[upd.Key] = upd.Value;
}
ハッシュテーブルの代わりにディクショナリを使用していて、キーのタイプがわかっている場合、この例外を回避するためにキーコレクションのコピーを作成する最も簡単な方法は次のとおりです。
foreach (string key in new List<string>(dictionary.Keys))
実際には変更していないのに、繰り返し処理しているコレクションを変更したという例外が表示されるのはなぜですか?
内部的に、Hashtableクラスにはバージョンフィールドがあります。 Add、Insert、およびRemoveメソッドは、このバージョンをインクリメントします。 Hashtableが公開するコレクションのいずれかに列挙子を作成すると、列挙子オブジェクトにはHashtableの現在のバージョンが含まれます。列挙子のMoveNextメソッドは、列挙子のバージョンをHashtableのバージョンと照合し、それらが等しくない場合、表示されているInvalidOperationExceptionをスローします。
これは、ハッシュテーブルが変更されたかどうかを判断するための非常に単純なメカニズムです。実際、それは少し単純すぎます。 Keysコレクションは実際に独自のバージョンを維持する必要があり、そのGetEnumeratorメソッドはコレクションのバージョンをHashtableのバージョンではなく列挙子に保存する必要があります。
このアプローチには、さらに微妙な設計上の欠陥があります。バージョンはInt32です。 UpdateVersionメソッドは境界チェックを行いません。したがって、Hashtableと列挙子のバージョンが同じであっても、Hashtableに正確な数の修正( Int32.MaxValue
を2回与える、または与える)を正確に行うと、可能になります。列挙子を作成してからHashtableを根本的に変更しました。そのため、MoveNextメソッドは、例外がスローされても例外をスローせず、予期しない結果が発生します。
最も簡単な方法は、キーを別のコレクションにコピーし、代わりにそのコレクションを反復処理することです。
.NET 3.5を使用していますか?もしそうなら、LINQは物事を少し簡単にします。
重要な部分は ToArray()メソッドです
var dictionary = new Dictionary<string, string>();
foreach(var key in dictionary.Keys.ToArray())
{
dictionary[key] = "new value";
}
コレクションを列挙している間、コレクションに格納されているアイテムのセットを変更することはできません。ほとんどの場合、イテレータの生活は非常に困難になるためです。コレクションがバランスのとれたツリーを表し、挿入後に回転する可能性がある場合を考えてください。列挙には、見たものを追跡するもっともらしい方法がありません。
ただし、値を更新しようとしている場合は、次のように記述できます。
deEntry.Value = sValue
ここで値を更新しても、列挙子には影響しません。
これは辞書内で行った方法です。 dictのすべての値をfalseにリセットします:
Dictionary<string,bool> dict = new Dictionary<string,bool>();
for (int i = 0; i < dict.Count; i++)
{
string key = dict.ElementAt(i).Key;
dict[key] = false;
}
ハッシュテーブル内のアイテムをループしている理由によって異なります。しかし、代わりにキーを繰り返し処理することができるでしょう。だから
foreach (String sKey in htSettings_m.Keys)
{ // Get value from Registry and assign to sValue.
// ...
// Change value in hashtable.
htSettings_m[sKey] = sValue;
}
もう1つのオプションは、新しいHashTableを作成することです。最初の項目を繰り返し処理し、2番目の項目に項目を追加してから、元の項目を新しい項目に置き換えます。
ただし、キーをループすると、オブジェクトの割り当てが少なくなります。
List<string> keyList = htSettings_m.Keys.Cast<string>().ToList();
foreach (string key in keyList) {
他の回答と同じですが、キーを取得するのに1行が好きです。
配列に変換する:
private Hashtable htSettings_m = new Hashtable();
htSettings_m.Add("SizeWidth", "728");
htSettings_m.Add("SizeHeight", "450");
string sKey = "";
string sValue = "";
ArrayList htSettings_ary = new ArrayList(htSettings_m.Keys)
foreach (DictionaryEntry deEntry in htSettings_ary)
{
// Get value from Registry and assign to sValue.
// ...
// Change value in hashtable.
sKey = deEntry.Key.ToString();
htSettings_m[sKey] = sValue;
}
private Hashtable htSettings_m = new Hashtable();
htSettings_m.Add("SizeWidth", "728");
htSettings_m.Add("SizeHeight", "450");
string sValue = "";
foreach (string sKey in htSettings_m.Keys)
{
// Get value from Registry and assign to sValue
// ...
// Change value in hashtable.
htSettings_m[sKey] = sValue;
}
Hashtable.Keysコレクションを使用できますか?ハッシュテーブルを変更している間、それを列挙することは可能かもしれません。しかし、それは単なる推測です...