如何在循环中更新 C# 哈希表?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/326757/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 23:19:08  来源:igfitidea点击:

How to update C# hashtable in a loop?

c#data-structuresloopshashtable

提问by z-boss

I'm trying to update a hashtable in a loop but getting an error: System.InvalidOperationException: Collection was modified; enumeration operation may not execute.

我正在尝试在循环中更新哈希表,但出现错误: 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;
}

Is there way around it or maybe there is a better data structure for such purpose?

有没有办法解决它,或者可能有更好的数据结构用于这种目的?

采纳答案by keithwarren7

you could read the collection of keys into another IEnumerable instance first, then foreach over that list

您可以先将键集合读入另一个 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]);
        }

回答by Rob Walker

You cannot change the set of items stored in a collection while you are enumerating over it, since that makes life very difficult for the iterator in most cases. Consider the case where the collection represents a balanced tree, and may well undergo rotations after an insert. The enumerate would have no plausible way of keeping track of what it has seen.

在枚举集合时,您不能更改存储在集合中的项目集,因为在大多数情况下,这会使迭代器的工作变得非常困难。考虑集合代表平衡树的情况,并且很可能在插入后进行旋转。枚举将没有合理的方式来跟踪它所看到的内容。

However, if you are just trying to update the value then you can write:

但是,如果您只是想更新该值,则可以编写:

deEntry.Value = sValue

Updating the value here has no impact on the enumerator.

更新此处的值对枚举器没有影响。

回答by Davy Landman

In concept I would do:

在概念上,我会这样做:

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;
}

回答by Vilx-

Maybe you can use Hashtable.Keys collection? Enumerating through that might be possible while changing the Hashtable. But it's only a guess...

也许你可以使用 Hashtable.Keys 集合?在更改 Hashtable 时可以通过枚举进行枚举。但这只是猜测...

回答by Stephen Martin

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;    
}

回答by pipTheGeek

It depends on why you are looping through the items in the hashtable. But you would probably be able to iterate throught the keys instead. So

这取决于您为什么要遍历哈希表中的项目。但是您可能可以通过密钥进行迭代。所以

foreach (String sKey in htSettings_m.Keys)
{   // Get value from Registry and assign to sValue.
    // ...    
    // Change value in hashtable.
    htSettings_m[sKey] = sValue;
}

The other option is to create a new HashTable. Iterate through the first while adding items to the second then replace the original with the new one.
Looping through the keys requires less object allocations though.

另一种选择是创建一个新的 HashTable。迭代第一个,同时将项目添加到第二个,然后用新的替换原来的。
不过,遍历键需要较少的对象分配。

回答by Jon Skeet

The simplest way is to copy the keys into a separate collection, then iterate through that instead.

最简单的方法是将键复制到一个单独的集合中,然后迭代它。

Are you using .NET 3.5? If so, LINQ makes things a little bit easier.

您在使用 .NET 3.5 吗?如果是这样,LINQ 会让事情变得更容易一些。

回答by Robert Rossney

If you're using a Dictionary instead of a Hashtable, so that the type of the keys is known, the easiest way to make a copy of the Keys collection to avoid this exception is:

如果您使用的是 Dictionary 而不是 Hashtable,以便知道键的类型,那么制作 Keys 集合的副本以避免此异常的最简单方法是:

foreach (string key in new List<string>(dictionary.Keys))

Why are you getting an exception telling you that you've modified the collection you're iterating over, when in fact you haven't?

为什么你会收到一个异常,告诉你你已经修改了你正在迭代的集合,而实际上你还没有?

Internally, the Hashtable class has a version field. The Add, Insert, and Remove methods increment this version. When you create an enumerator on any of the collections that the Hashtable exposes, the enumerator object includes the current version of the Hashtable. The enumerator's MoveNext method checks the enumerator's version against the Hashtable's, and if they're not equal, it throws the InvalidOperationException you're seeing.

在内部,Hashtable 类有一个版本字段。Add、Insert 和 Remove 方法增加这个版本。在 Hashtable 公开的任何集合上创建枚举器时,枚举器对象包括 Hashtable 的当前版本。枚举器的 MoveNext 方法根据 Hashtable 的版本检查枚举器的版本,如果它们不相等,则会抛出您看到的 InvalidOperationException。

This is a very simple mechanism for determining whether or not the Hashtable has been modified. In fact it's a little too simple. The Keys collection really ought to maintain its own version, and its GetEnumerator method ought to save the collection's version in the enumerator, not the Hashtable's version.

这是一种非常简单的机制,用于确定 Hashtable 是否已被修改。其实有点太简单了。Keys 集合确实应该维护它自己的版本,它的 GetEnumerator 方法应该在枚举器中保存集合的版本,而不是 Hashtable 的版本。

There's another, subtler design defect in this approach. The version is an Int32. The UpdateVersion method does no bounds checking. It's therefore possible, if you make exactly the right number of modifications to the Hashtable (2 times Int32.MaxValue, give or take), for the version on the Hashtable and the enumerator to be the same even though you've radically changed the Hashtable since creating the enumerator. So the MoveNext method won't throw the exception even though it should, and you'll get unexpected results.

这种方法还有另一个更微妙的设计缺陷。版本是 Int32。UpdateVersion 方法不进行边界检查。因此,如果您对 Hashtable 进行正确数量的修改(2 次Int32.MaxValue,给予或接受),则有可能使 Hashtable 和枚举器上的版本相同,即使自创建以来您已经彻底更改了 Hashtable枚举器。因此,即使 MoveNext 方法应该抛出异常,也不会抛出异常,并且您会得到意想不到的结果。

回答by Gian Marco Gherardi

The key part is the ToArray()method

关键部分是ToArray()方法

var dictionary = new Dictionary<string, string>();
foreach(var key in dictionary.Keys.ToArray())
{
    dictionary[key] = "new value";
}

回答by Okan

This is how I did it within a dictionary; resets every value in dict to false:

这就是我在字典中所做的;将 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;
}