Операции Descendants и DescendantsAndSelf

25

Descendants

Операция Descendants может быть вызвана на последовательности элементов или документов и возвращает последовательность элементов, содержащую все элементы-потомки каждого исходного элемента или документа.

Операция Descendants имеет два прототипа, описанные ниже:

Первый прототип Descendants
public static IEnumerable<XElement> Descendants<T> (
   this IEnumerable<T> source 
   ) where T : XContainer

Эта версия вызывается на последовательности элементов или документов и возвращает последовательность документов, содержащую потомков каждого исходного элемента или документа.

Отличие этой операции от метода XContainer.Descendants в том, что этот метод вызывается на последовательности элементов или документов, а не на отдельном элементе или документе.

Второй прототип Descendants
public static IEnumerable<XElement> Descendants<T> ( 
    this IEnumerable<T> source, 
    XName name 
    ) where T : XContainer

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

Для демонстрации первого прототипа используется в основном тот же пример, что и для операции DescendantNodes из предыдущей статьи, вызывая вместо нее операцию Descendants. Вывод должен быть таким же, только без узлов, не являющихся элементами. То есть комментарии в выводе не появятся:

XDocument xDoc = new XDocument(
             new XElement("Employees",
               new XElement("Employee",
                 new XAttribute("type", "Programmer"),
                 new XComment("Это программист"),
                 new XElement("FirstName", "Alex"), 
                 new XElement("LastName", "Erohin")),
               new XElement("Employee",
                 new XAttribute("type", "Editor"),
                 new XElement("FirstName", "Elena"), new XElement("LastName", "Volkova"))));

            // Получаем все элементы Employee
            IEnumerable<XElement> elements = xDoc.Element("Employees").Elements("Employee");

            foreach (XElement e in elements)
            {
                Console.WriteLine("Исходный элемент: {0} значение = {1}", e.Name, e.Value);
            }

            Console.WriteLine();

            foreach (XNode node in elements.Descendants())
            {
                Console.WriteLine("Узел-потомок: " + node);
            }

В данном примере выводятся только элементы-потомки двух элементов Employee. Результат работы этого примера выглядит следующим образом:

Вызов первого прототипа Descendants

Сравнивая эти результаты с примером операции DescendantNodes из предыдущей статьи, можно заметить некоторые неожиданные отличия. Конечно, потомки помечены как элементы, а не узлы, и комментарий отсутствует, но при этом отсутствуют также узлы-потомки, такие как Alex и Elena. Разумеется, эти узлы также не являются элементами. Это объекты XText. API-интерфейс LINQ to XML обрабатывает текстовые узлы настолько гладко, что о них легко забыть.

Для демонстрации второго прототипа используется тот же код, что и в первом примере, за исключением дополнительного указания имени, которому должны соответствовать элементы-потомки, чтобы второй прототип операции Descendant вернул их:

...
foreach (XNode node in elements.Descendants("LastName"))
{
        Console.WriteLine("Узел-потомок: " + node);
}

Результат этого примера выглядит следующим образом:

Вызов второго прототипа Descendants

Как и можно было ожидать, возвращены только элементы LastName.

DescendantsAndSelf

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

Операция DescendantsAndSelf имеет два прототипа, которые описаны ниже:

Первый прототип DescendantsAndSelf
public static IEnumerable<XElement> DescendantsAndSelf ( 
     this IEnumerable<XElement> source
     )

Эта версия вызывается на последовательности элементов и возвращает последовательность элементов, содержащую каждый исходный элемент и его потомков.

Второй прототип DescendantsAndSelf
public static IEnumerable<XElement> DescendantsAndSelf ( 
         this IEnumerable<XElement> source, 
         XName name)

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

Для демонстрации работы первой версии DescendantsAndSelf используется тот же код, что и в примере применения первого прототипа операции Descendants, с заменой ее на операцию DescendantsAndSelf, как показано ниже:

...
foreach (XNode node in elements.DescendantsAndSelf())
{
        Console.WriteLine("Узел-потомок: " + node);
}

После запуска этого примера должны отобразиться все исходные элементы и их элементы-потомки. Результаты показаны ниже:

Вызов первого прототипа DescendantsAndSelf

Таким образом, вывод тот же, что и у операции Descendants, за исключением того, что он также включает сами исходные элементы — элементы Employee. Пусть не вводит в заблуждение наличие комментария в результатах. Это не потому, что он был возвращен операцией DescendantsAndSelf, а потому, что отображается элемент Employee, который был возвращен операцией.

Для демонстрации второго прототипа DescendantsAndSelf применяется тот же пример, что и для первого прототипа, но с добавлением имени, которому должны соответствовать возвращаемые элементы:

...
foreach (XNode node in elements.DescendantsAndSelf("FirstName"))
{
            Console.WriteLine("Узел-потомок: " + node);
}

Результат этого примера выглядит так:

Вызов второго прототипа DescendantsAndSelf

Результат включает только элементы-потомки, соответствующие указанному имени. Здесь не слишком очевидно, что была вызвана операция DescendantsAndSelf, а не Descendants, поскольку исходные элементы не возвращены по причине несоответствия их имени указанному.

Опять-таки, как и со всеми операциями, которые возвращают элементы из множества уровней дерева XML, принимающих аргумент — имя, которому должны соответствовать возвращаемые элементы, вряд ли понадобятся AndSelf-версии операций, поскольку маловероятно, что будет много уровней одноименных операций.

Пройди тесты
Лучший чат для C# программистов