class Person
{
//конструкторы
public Person(){name =""; id=0; salary=0.0;}
public Person(string name){this.name = name;}
public Person (string name, int id, double salary)
{this.name = name; this.id=id; this.salary = salary;}
public Person (Person pers)
{this.name = pers.name; this.id = pers.id;
this.salary = pers.salary;}
//методы
public void ToPerson(string mes)
{
this.message = mes;
Console.WriteLine("{0}, {l}",name, message);
}
//свойства
private string name;
private int id;
private double salary;
private string message;
//доступ к свойствам
public string Name
{get {return(name);} set {name = value;}}
public double Salary
{get {return(salary);} set {salary = value;}}
public int Id
{get {return(id);} set {id = value;}}
}//class Person
Класс Person устроен обычным способом: у него несколько перегруженных конструкторов, закрытые поля и процедуры-свойства для доступа к ним. Особо обратить внимание прошу на метод класса ToPerson, сигнатура которого совпадает с сигнатурой класса, определенной введенным ранее делегатом MesToPers. Посмотрите, как клиент класса может связать этот метод с экземпляром делегата, определенного самим клиентом:
Person man2 = new Person("Владимир");
MesToPers mestopers = new MesToPers(man2.ToPerson);
mestopers("пора работать!");
Обратите внимание, что поскольку метод ToPerson не является статическим методом, то при связывании необходимо передать и объект, вызывающий метод. Более того, переданный объект становится доступным экземпляру делегата. Отсюда сразу же становится ясным, что экземпляры делегата — это не просто указатели на функцию, а более сложно организованные структуры. Они, по крайней мере, содержат пару указателей на метод и на объект, вызвавший метод. Вызываемый метод в своей работе использует как информацию, передаваемую ему через аргументы метода, так и информацию, хранящуюся в полях объекта. В данном примере переданное сообщение "пора работать" присоединится к имени объекта, и результирующая строка будет выдана на печать. В тех случаях, когда метод, связываемый с экземпляром делегата, не использует информацию объекта, этот метод может быть объявлен как статический метод класса. Таким образом, инициализировать экземпляры делегата можно как статическими, так и динамическими методами, связанными с конкретными объектами.
Последние три строки были добавлены в вышеприведенную тестирующую процедуру. Взгляните на результаты ее работы.
Рис. 20.1. Объявление делегатов и создание их экземпляров
Одно из наиболее важных применений делегатов связано с функциями высших порядков. Функцией высшего порядка называется такая функция (метод) класса, у которой один или несколько аргументов принадлежат к функциональному типу. Без этих функций в программировании обойтись довольно трудно. Классическим примером является функция вычисления интеграла, у которой один из аргументов задает подынтегральную функцию. Другим примером может служить функция, сортирующая объекты. Аргументом ее является функция Compare, сравнивающая два объекта. В зависимости оттого, какая функция сравнения будет передана на вход функции сортировки, объекты будут сортироваться по-разному, например, по имени, или по ключу, или по нескольким полям. Вариантов может быть много, и они определяются классом, описывающим сортируемые объекты.
Вычисление интеграла
Давайте более подробно рассмотрим ситуацию с функциями высшего порядка на примере задачи вычисления определенного интеграла с заданной точностью. С этой целью создадим класс, в котором будет описан делегат, определяющий контракт, коему должны удовлетворять подынтегральные функции. В этом же классе определим метод, вычисляющий интеграл. По сути самой задачи этот метод представляет собой функцию высшего порядка. Приведу программный код, описывающий класс:
public class HighOrderIntegral
{
//delegate
public delegate double SublntegralFun(double x);
public double Evallntegral(double a, double b, double eps,SublntegralFun sif)