Если вы используете словарь вместо Hashtable, чтобы тип ключей был известен, самый простой способ сделать копию коллекции Keys, чтобы избежать этого исключения:
foreach (string key in new List<string>(dictionary.Keys))
Почему вы получаете исключение, сообщающее, что вы изменили коллекцию, по которой вы перебираете, а на самом деле это не так?
Внутри класса Hashtable есть поле версии. Методы Add, Insert и Remove увеличивают эту версию. Когда вы создаете перечислитель в любой из коллекций, предоставляемых Hashtable, объект перечислителя включает текущую версию Hashtable. Метод MoveNext перечислителя проверяет версию перечислителя на соответствие Hashtable, и если они не равны, он генерирует исключение InvalidOperationException, которое вы видите.
Это очень простой механизм определения того, была ли изменена Hashtable. На самом деле это слишком просто. Коллекция Keys действительно должна поддерживать свою собственную версию, а метод GetEnumerator должен сохранять версию коллекции в перечислителе, а не версию Hashtable.
В этом подходе есть еще один, более тонкий дефект дизайна. Версия Int32. Метод UpdateVersion не выполняет проверку границ. Поэтому возможно, если вы сделаете точно правильное количество изменений в Hashtable (2 раза Int32.MaxValue
, дай или возьми), чтобы версия в Hashtable и перечислителе была одинаковой, даже если вы радикально изменили Hashtable с момента создания перечислителя. Таким образом, метод MoveNext не будет генерировать исключение, даже если он должен, и вы получите неожиданные результаты.