Удалил бы ключ из словаря в foreach, вызвал бы проблему? или мне лучше построить новый словарь?

например:

1.

         foreach (var item in myDic)
                {
                  if (item.value == 42)
                        myDic.remove(item.key);
                }

будет ли итератор корректно работать независимо от того, как могут отображаться выражения во внутренних скобках myDic?

2.

    var newDic = myDic.where(x=>x.value!=42).ToDictionary(x=>x.key,x=>x.value);

Является ли второй подход хорошей практикой? функциональное программирование и неизменяемость?

Ответ 1

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

Второй подход - хорошая мысль, но словари С# являются изменяемыми и не являются ни идиоматическими, ни эффективными, чтобы копировать их, если вы можете сделать то же самое с мутацией.

Это типичный способ:

var itemsToRemove = myDic.Where(f => f.Value == 42).ToArray();
foreach (var item in itemsToRemove)
    myDic.Remove(item.Key);

EDIT: ответ на ваш вопрос в комментариях. Вот как работает пример в другом вопросе:

myList = myList.where(x=>x>10).select(x=>x-10);

Эта строка кода ничего не запускает; это совершенно лениво. Скажем, ради аргумента, что после этого мы имеем foreach, чтобы он больше напоминал этот вопрос.

foreach (int n in myList)
    Console.WriteLine(n);

Когда это выполняется, вот что произойдет на каждой итерации:

  • Вызов MoveNext в перечислении
  • Перечислитель находит следующее значение больше десяти
  • Затем оно принимает это значение минус десять и устанавливает для свойства Current значение
  • Привязывает свойство Current к переменной n
  • Console.WriteLine это

Вы можете видеть, что нет никакой тайны и бесконечной петли, и ничего.

Теперь сравните с моим примером, предположим, что мы оставили значение ToArray.

var itemsToRemove = myDic.Where(f => f.Value == 42);
foreach (var item in itemsToRemove)
    myDic.Remove(item.Key);
  • Вызов MoveNext в перечислении
  • Перечислитель находит следующую пару со значением 42 и устанавливает для свойства Current значение
  • Привязывает свойство Current к переменной item
  • Remove это

Это не работает, потому что, хотя он отлично подходит для WriteLine что-то из коллекции, когда у вас есть открытый счетчик, вам не разрешено Remove что-то из коллекции, пока вы открываете счетчик он.

Если вы вызываете ToArray спереди, вы начинаете с перечисления по словарю и заполнения массива. Когда мы переходим к foreach, оператор foreach имеет перечислитель, открытый в массиве, а не словарь. Вы можете удалить из словаря, когда вы перебираете массив.

Ответ 2

Также вы можете перебирать копию вашей коллекции:

foreach (var item in myDic.ToList())
{
    if (item.value == 42)
    myDic.remove(item.key);
}

уведомление myDic.ToList() в foreach.