Как обновить хеш-таблицу C# в цикле?

Я пытаюсь обновить хэш-таблицу в цикле, но получаю сообщение об ошибке: System.InvalidOperationException: Коллекция была изменена; операция перечисления может не выполняться.

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

Есть ли способ обойти это или, может быть, есть лучшая структура данных для этой цели?

Считайте, что это дублированный вопрос, см .: stackoverflow.com/questions/287195/…

Gavin Miller 29.11.2008 01:03
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
11
1
41 436
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

Вы не можете изменить набор элементов, хранящихся в коллекции, во время перечисления по ней, поскольку в большинстве случаев это очень усложняет жизнь итератору. Рассмотрим случай, когда коллекция представляет собой сбалансированное дерево и может претерпевать вращения после вставки. У счетчика не было бы надежного способа отслеживать то, что он видел.

Однако, если вы просто пытаетесь обновить значение, вы можете написать:

deEntry.Value = sValue

Обновление значения здесь не влияет на счетчик.

Это не компилируется: нельзя изменять члены deEntry, потому что это «переменная итерации foreach»

z-boss 29.11.2008 01:08

По идее я бы сделал:

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

Может быть, вы можете использовать коллекцию Hashtable.Keys? Перечисление через это может быть возможно при изменении Hashtable. Но это только предположение ...

Ответ принят как подходящий

вы можете сначала прочитать коллекцию ключей в другом экземпляре 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]);
        }
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 использует один и тот же тип перечислителя для Hashtable и для его ключей. На мой взгляд, реализация с серьезными недостатками.

Stephen Martin 29.11.2008 01:24

Нет, проблема не в типе перечислителя - а в том, что свойство Keys не принимает копировать всех ключей, оно просто выполняет итерацию по базовой коллекции. Когда вы необходимость, чтобы сделать копию, сделайте это явно. Я лично хочу и ожидаю именно такого поведения.

Jon Skeet 29.11.2008 01:45

Я должен с вами не согласиться: коллекция Keys - это коллекция Keys, а не Hashtable, перечислитель Keys должен быть чувствителен к изменениям только в коллекции Keys, а не в Hashtable в целом.

Stephen Martin 29.11.2008 02:24

Я думаю, что это дефект конструкции класса Hashtable. См. Подробности в моем ответе.

Robert Rossney 29.11.2008 07:24

Это зависит от того, почему вы перебираете элементы в хеш-таблице. Но вместо этого вы, вероятно, сможете перебирать ключи. Так

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

Другой вариант - создать новую HashTable. Пройдите по первому, добавляя элементы ко второму, а затем замените оригинал новым. Однако перебор ключей требует меньшего выделения объектов.

Самый простой способ - скопировать ключи в отдельную коллекцию, а затем выполнить итерацию по ней.

Вы используете .NET 3.5? Если это так, LINQ немного упрощает задачу.

Если вы используете Dictionary вместо Hashtable, чтобы известен тип ключей, самый простой способ сделать копию коллекции Keys, чтобы избежать этого исключения:

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

Почему вы получаете исключение, сообщающее вам, что вы изменили коллекцию, которую повторяете, когда на самом деле это не так?

Внутри класса Hashtable есть поле версии. Методы Add, Insert и Remove увеличивают эту версию. Когда вы создаете перечислитель для любой из коллекций, которые предоставляет Hashtable, объект перечислителя включает текущую версию Hashtable. Метод MoveNext перечислителя проверяет версию перечислителя на соответствие с Hashtable и, если они не равны, генерирует InvalidOperationException, которое вы видите.

Это очень простой механизм для определения того, была ли изменена хеш-таблица. На самом деле это слишком просто. Коллекция Keys действительно должна поддерживать свою собственную версию, а ее метод GetEnumerator должен сохранять версию коллекции в перечислителе, а не версию Hashtable.

В этом подходе есть еще один, более тонкий недостаток дизайна. Версия - Int32. Метод UpdateVersion не проверяет границы. Следовательно, возможно, если вы сделаете точно нужное количество модификаций в Hashtable (2 раза Int32.MaxValue, плюс-минус), чтобы версия в Hashtable и перечислитель были одинаковыми, даже если вы радикально изменили Hashtable с момента создания счетчик. Таким образом, метод MoveNext не будет генерировать исключение, даже если должен, и вы получите неожиданные результаты.

Ключевой частью является метод ToArray ()

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

намного более простое решение, чем текущее топовое.

Lars 26.01.2013 23:40

Вот как я это сделал в словаре; сбрасывает каждое значение в 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;
}
List<string> keyList = htSettings_m.Keys.Cast<string>().ToList();
foreach (string key in keyList) {

Это то же самое, что и другие ответы, но мне нравится одна строка, чтобы получить ключи.

Преобразуйте его в массив:

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

Другие вопросы по теме