Pergunta

Eu estou tentando atualizar um hashtable em um loop, mas recebendo um erro: System.InvalidOperationException: Coleção foi modificada; operação de enumeração não pode executar.

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

Existe maneira de contornar isso ou talvez haja uma melhor estrutura de dados para esse fim?

Foi útil?

Solução

Você pode ler a coleção de chaves em outra instância IEnumerable primeiro, então foreach sobre essa lista

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

Outras dicas

No conceito que eu faria:

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

Se você estiver usando um dicionário em vez de um Hashtable, de modo que o tipo das chaves é conhecido, a maneira mais fácil de fazer uma cópia das chaves coleção para evitar essa exceção é:

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

Por que você está recebendo uma exceção dizendo que você tenha modificado a coleção que você está interagindo sobre, quando na verdade você não tem?

Internamente, a classe Hashtable tem um campo de versão. O Add, Insert, e métodos Remover incrementar esta versão. Quando você cria um enumerador em qualquer uma das coleções que os expõe Hashtable, o objeto enumerador inclui a versão atual do Hashtable. método MoveNext cheques do enumerador versão do recenseador contra o Hashtable de, e se eles não são iguais, ele lança o InvalidOperationException que você está vendo.

Este é um mecanismo muito simples para determinar se ou não o Hashtable foi modificado. Na verdade, é um pouco simples demais. A coleção Chaves realmente deveria manter a sua própria versão, e seu método GetEnumerator deve salvar a versão da coleção do recenseador, não a versão do Hashtable.

Há uma outra, defeito de design mais sutil nesta abordagem. A versão é um Int32. O método UpdateVersion faz nenhuma verificação de limites. É possível, portanto, se você fizer exatamente o número certo de modificações para o Hashtable (2 vezes Int32.MaxValue, mais ou menos), para a versão na tabela de hash e o enumerador para ser o mesmo, embora você mudou radicalmente o Hashtable desde a criação o enumerador. Assim, o método MoveNext não vai lançar a exceção mesmo que deveria, e você vai ter resultados inesperados.

A maneira mais simples é copiar as chaves em uma coleção separada, então iterate através dessa vez.

Você está usando .NET 3.5? Se assim for, LINQ torna as coisas um pouco mais fácil.

A parte fundamental é a ToArray () método

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

Você não pode alterar o conjunto de itens armazenados em uma coleção enquanto você está enumerando sobre ele, uma vez que torna a vida muito difícil para o iterador na maioria dos casos. Consideremos o caso em que a recolha representa uma árvore equilibrada, e pode assim sofrer rotações depois de uma inserção. O enumerate não teria nenhuma maneira plausível de se manter a par do que tem visto.

No entanto, se você está apenas tentando atualizar o valor, em seguida, você pode escrever:

deEntry.Value = sValue

A actualização do valor aqui não tem impacto sobre o enumerador.

Isto é como eu fiz isso dentro de um dicionário; repõe todos os valores em dict como 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;
}

Depende por que você está looping através dos itens na tabela hash. Mas você provavelmente seria capaz de iterate throught as chaves em seu lugar. Então

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

A outra opção é criar uma nova HashTable. Percorrer a primeira ao adicionar itens ao segundo, em seguida, substituir o original com o novo.
Looping através das teclas requer menos alocações de objetos embora.

List<string> keyList = htSettings_m.Keys.Cast<string>().ToList();
foreach (string key in keyList) {

É o mesmo que as outras respostas, mas eu como a linha para obter as chaves.

Converter-lo para um array:

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

Talvez você possa usar coleção Hashtable.Keys? Enumerando através de que pode ser possível ao alterar o Hashtable. Mas é só um palpite ...

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top