Секреты LINQ. Автогенерируемые свойства. Инициализаторы объектов и коллекций, страница 12

В классе BinaryTree<T> нет средств индексирования элементов, необходимых для реального дерева поиска, в нем также нет методов поиска узлов, нет методов записи данных в файл и чтения из него. Пример служит для иллюстрирации принципов разработки универсальных классов. Рассмотрим, например, как добавить в этот класс метод поиска заданного элемента и отображения пути к нему, начиная от корня.

Так как в этом методе используется рекурсия, то в нем нельзя применить прием с итеративным блоком yield. Документация говорит, что yield нельзя использовать в рекурсивных функциях. Путь к искомому узлу  будемхранить в generic-коллекции List<T>.

¨  Добавьте в класс BinaryTree<T> следующее объявление: List<T> path;

¨  Добавьте конструктор с кодом создания списка.

public BinaryTree ()

{

path = new List<T> ();

}

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

public List<T> FindPath (T item)

{

path.Clear ();

FindPath (item, root);

return path;

}

¨  Добавьте скрытую версию рекурсивного метода FindPath для поиска узла и формирования списка List<T>, который будет содержать путь к узлу дерева item. При повторных вызовах путь необходимо обнулять и формировать заново. В процедуре поиска, также как и в методе Add, мы используем свойство сравниваемости элементов (см. обращение к CompareTo).

void FindPath (T item, Node<T> node)

{

if (node != null)

{

path.Add (node.data);

if (item.CompareTo (node.data) < 0)

FindPath (item, node.left);

else

FindPath (item, node.right);

}

}

¨  Добавьте в метод TestBinaryTree код вызова FindPath, запустите приложение и проанализируйте результат.

Console.WriteLine ("\n\nTesting FindPath(21)\n");

foreach (var item in tree.FindPath (21))

Console.Write (item + "->");

Расширение функциональности существующих типов (extension methods)

Как было указано выше, в языке C# появились новые синтаксические конструкции. Рассмотрим одну из них. Она позволяет расширить функциональность существующего типа, даже если он имеет описатель sealed, каким, например, является класс String. Подобный прием давно существует в языке JScript. Теперь, благодаря усилиям команды Anders Hejlsberg'а (создателя языка C#), такая возможность появилась и в C#.

Представим, что нас не устраивает функциональность класса Object и мы хотим ее расширить, но не посредством создания производного класса (public class MyObject : Object {}), а путем добавления нового метода в уже существующий класс Object. В версии .NET Framework 1.0 такая мысль была бы крамольной. Теперь это можно сделать, добавив пару строк кода. Приведем формальное определение extension-метода.

Extension method: A static method that can be invoked by using instance method syntax. In effect, extension methods make it possible to extend existing (and constructed) types with additional methods.

Здесь важно то, что вызов extension method'а будет выглядеть вполне естественно — так, как если бы он изначально присутствовал в расширяемом классе, то есть, был обычным нестатическим методом.

Напомним, что обычные нестатические методы называют instance methods, их вызов производится для объекта, а не класса, например: obj.Method(). Обычные статические методы называют class methods, их вызов происходит для класса, а не для объекта, например: Class.Method().

Для создания статического необычного (или extension-) метода следует поместить его в статический класс, и особым способом определить первый параметр. Имя статического класса с расширяющим методом произвольно, а имя метода задает расширение — новую, добавляемую функциональность.

Рассмотрим пример, который расширяет класс Object и добавляет в него extension-метод IsIn, возвращающий результат поиска произвольного объекта в произвольной коллекции. Этот пример часто используется для иллюстрации логики cоздания расширений.