Поведение, которое вы наблюдаете, может быть продемонстрировано более просто следующим образом. Следующий модульный тест будет успешным (демонстрационная скрипка здесь ):
XmlNodeList nodelist = doc.SelectNodes("*/*"); // Select all children of the root node
RemoveChildren(doc.DocumentElement);
Assert.IsTrue(nodelist.Count == 0); // Passes
В то время как следующее не удастся:
XmlNodeList nodelist = doc.SelectNodes("*/*"); // Select all children of the root node
Assert.IsTrue(nodelist.Count > 0); // Passes
RemoveChildren(doc.DocumentElement);
Assert.IsTrue(nodelist.Count == 0); // FAILS!?
Почему может быть добавлено простое nodelist.Count
перед удалением некоторых узлов вызывает несоответствие в содержимом nodelist
после удаления узлов?
Как выясняется, не существует определенного поведения для XmlNodeList
, возвращаемого SelectNodes()
в этой ситуации. Из замечаний по документации для XmlNode.SelectNodes()
:
Объект XmlNodeList
, возвращаемый этим методом, будет действительным в то время как базовый документ остается без изменений. Если базовый документ изменяется, могут быть возвращены неожиданные результаты (исключение не будет выдано).
Такие "неожиданные результаты" - это то, что вы наблюдаете. После вызова RemoveChildren()
содержимое nodelist
не указывается и не гарантируется. Net. (На самом деле, похоже, что возвращаемое XmlNodeList
использует механизм lazyvaluation . Как только список узлов посчитан или итерирован (но не раньше), запрос XPath оценивается один раз и только один раз. и результаты кэшируются и впоследствии используются повторно.)
Это документированное ограничение на списки узлов, возвращаемых запросами XPath, отличается от общей документации для XmlNodeList
, которая гласит:
Изменения в потомках объекта узла, из которого была создана коллекция XmlNodeList
, немедленно отражаются в узлах, возвращаемых свойствами и методами XmlNodeList
.
Это замечание выглядит как применяется только к XmlNodeList
дочерним спискам, возвращаемым методами XmlNode
объектов DOM.
Чтобы избежать несоответствия , вы можете явно материализовать XmlNodeList
в a List<XmlNode>
сразу после оценки, вот так:
var nodelist = doc.SelectNodes("*/*").Cast<XmlNode>().ToList();
Демонстрационная скрипка здесь .
Переключение на LINQ на XML будет другой вариант, так как с ним обычно проще работать, чем с более старым XmlDocument
API.