Почему мой код C# Xml работает только при перечислении переменных enumerable - PullRequest
2 голосов
/ 16 июня 2020

Я работаю над кодом, который форматирует файл XML, чтобы узлы подпапок фактически были вложены в их родительский узел. Исходный XML имеет каждую папку как отдельный дочерний узел в root вместо правильного дерева, как вы ожидали бы с подпапками в их основных папках. Фрагмент кода, о котором идет речь:

// Load original XML

string sFile = "PathFile";
XmlDocument doc = new XmlDocument();
doc.Load(sFile);

var n = doc.DocumentElement.SelectNodes ("//*");   // Load all nodes into nodelist n
// int nCount = n.Count;                           // If uncommented code works

foreach(XmlNode x in n)
{ rest of the code }

Теперь у меня есть код, работающий правильно, но только иногда, даже без каких-либо изменений между запусками. Я сузил его до следующего: при отладке кода в Visual Studio все идет не так, если я просто запускаю код от начала до конца. Если я прервусь на полпути и посмотрю на атрибуты в XmlNodelist n (наведя на него курсор и увидев количество элементов), это действительно сработает. Обнаружив это, я добавил строку

int nCount = n.Count; 

, и теперь код работает без присмотра от начала до конца sh.

Что здесь происходит и как правильно решить эту проблему? Примечание: do c .Load Xml не работает с этим конкретным файлом.

Спасибо, загружает,

Thomas

1 Ответ

5 голосов
/ 16 июня 2020

Краткий ответ: из-за побочных эффектов при реализации XmlNodeList.

XmlNode.SelectNodes() возвращает XmlNodeList (технически XPathNodeList), что представляет собой «живой список» узлов, соответствующих выбору (в данном случае выбор XPath).

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

Однако, чтобы вернуть значение для свойства Count, XPathNodeList в основном необходимо найти каждый соответствующий узел и подсчитать их , поэтому он просматривает весь набор совпадений и помещает их все во внутренний список.

public override int Count {
    get {
        if (! done) {
            ReadUntil(Int32.MaxValue);
        }
        return list.Count;
    }
}

Я думаю, это объясняет то, что вы видите. Когда вы обращаетесь к свойству Count перед внесением изменений, оно создает весь список узлов в качестве побочного эффекта, так что этот список все еще заполняется, когда вы фактически проходите по ним.

Конечно, это было бы неразумно полагаться на это недокументированное поведение.

Вместо этого я советую вам фактически скопировать содержимое XmlNodeList в свой собственный список, а затем перебрать его:

string sFile = "PathFile";
XmlDocument doc = new XmlDocument();
doc.Load(sFile);

var allNodes = doc.DocumentElement
    .SelectNodes("//*")
    .OfType<XmlNode>()      // using System.Linq;
    .ToList();

foreach (XmlNode x in allNodes)
{
    // rest of the code 
}
...