Делегаты как свойства
В наших примерах рассматривалась ситуация, при которой в некотором классе объявлялись функции, удовлетворяющие контракту с делегатом, но создание экземпляров делегата и их инициирование функциями класса выполнялось в другом месте, там, где предполагалось вызывать соответствующие функции. Чаще всего, создание экземпляров удобнее возложить на класс, создающий требуемые функции. Более того, в этом классе делегат можно объявить как свойство класса, что позволяет "убить двух зайцев". Во-первых, с пользователей класса снимается забота создания делегатов, что требует некоторой квалификации, которой у пользователя может и не быть. Во-вторых, делегаты создаются динамически, в тот момент, когда они требуются. Это важно как при работе с функциями высших порядков, когда реализаций, например, подынтегральных функций, достаточно много, так и при работе с событиями класса, в основе которых лежат делегаты.
Рассмотрим пример, демонстрирующий и поясняющий эту возможность при работе с функциями высших порядков. Идея примера такова. Спроектируем два класса:
• класс объектов Person с полями: имя, идентификационный номер, зарплата. В этом классе определим различные реализации функции Compare, позволяющие сравнивать два объекта по имени, по номеру, по зарплате, по нескольким полям. Самое интересное, ради чего и строится данный пример: для каждой реализации Compare будет построена процедура-свойство, которая задает реализацию делегата, определенного в классе Persons;
• класс Persons будет играть роль контейнера объектов Person.
В этом классе будут определены операции над объектами. Среди операций нас, прежде всего, будет интересовать сортировка объектов, реализованная в виде функции высших порядков. Функциональный параметр будет задавать класс функций сравнения объектов, реализации которых находятся в классе Person. Делегат, определяющий класс функций сравнения, будет задан в классе Persons.
Теперь, когда задача ясна, приступим к ее реализации. Класс Person уже появлялся в наших примерах, поэтому он просто дополнен до нужной функциональности. Добавим методы сравнения двух объектов Person;
//методы сравнения
private static int CompareName(Person obj1, Person obj2)
{
return(string.Compare(obj1.name,obj 2.name));
}
private static int Compareld(Person obj1, Person obj2)
{
if(obj1.id > obj2.id) return(1);
else return(-1);
}
private static int CompareSalary(Person obj1, Person obj2)
{
if(obj1.salary > obj2.salary) return(1);
else if(obj1.salary < obj2.salary)return(-1);
else return(0);
}
private static int CompareSalaryName(Person obj1, Person obj2)
{
if(obj1.salary > obj2.salary) return(1);
else if(obj1.salary < obj2.salary)return (-1);
else return(string.Compare(obj1.name,obj2.name));
}
Заметьте, методы закрыты и, следовательно, недоступны извне. Их четыре, но могло бы быть и больше, при возрастании сложности объекта растет число таких методов. Все методы имеют одну и ту же сигнатуру и удовлетворяют контракту, заданному делегатом, который будет описан чуть позже. Для каждого метода необходимо построить экземпляр делегата, который будет задавать ссылку на метод. Поскольку не все экземпляры нужны одновременно, то хотелось бы строить их динамически, в тот момент, когда они понадобятся. Это можно сделать, причем непосредственно в классе Person.
Закрытые методы будем рассматривать как закрытые свойства и для каждого из них введем статическую процедуру-свойство, возвращающую в качестве результата экземпляр делегата со ссылкой на метод. Проще написать, чем объяснить на словах:
//делегаты как свойства
public static Persons.CompareItems SortByName
{
get {return(new Persons.CompareItems(CompareName));}
}
public static Persons.CompareItems SortById
}
get {return(new Persons.CompareItems(CompareId));
}
public static Persons.CompareItems SortBySalary
{
get {return(new Persons.CompareItems(CompareSalary));}
}
public static Persons.CompareItems SortBySalaryName
{
get {return(new Persons.CompareItems(CompareSalaryName));}
}
Всякий раз, когда будет запрошено, например, свойство SortByName класса Person, будет возвращен объект функционального класса Persons. CompareItems, задающий ссылку на метод CompareName класса Person. Объект будет создаваться динамически в момент запроса.
Класс Person полностью определен, и теперь давайте перейдем к определению контейнера, содержащего объекты Person. Начну с определения свойств класса Persons:
class Persons