📚 Hub Books: Онлайн-чтение книгРазная литератураИнтернет-журнал "Домашняя лаборатория", 2007 №9 - Журнал «Домашняя лаборатория»

Интернет-журнал "Домашняя лаборатория", 2007 №9 - Журнал «Домашняя лаборатория»

Шрифт:

-
+

Интервал:

-
+
1 ... 363 364 365 366 367 368 369 370 371 ... 415
Перейти на страницу:
char sign;

    //конструктор

    public OwnDel(char sign)

    {

         this.sign = sign;

         if (sign == '+')

         {p1 = new Proc(Plus);f1 = new Fun1(Plus1);}

         else

         {p1 = new Proc(Minus);f1 = new Fun1(Minus1);}

     }

}//class OwnDel

Прокомментирую этот текст.

• Первым делом объявлены три функциональных класса — три делегата: Proc, MesToPers, Fun1. Каждый из них описывает множество функций фиксированной сигнатуры.

• В классе OwnDel описаны четыре метода: Plus, Minus, Plus1, Minus1, сигнатуры которых соответствуют сигнатурам, задаваемых классами рrос и Fun1.

• Поля p1 и f1 класса OwnDel являются экземплярами классов рrос и Fun1.

• В конструкторе класса поля p1 и f1 связываются с конкретными методами Plus или Minus, Plus1 или Minus1. Связывание с той или иной функцией в данном случае определяется значением поля sign.

Заметьте, экземпляры делегатов можно рассматривать как ссылки (указатели на функции), а методы тех или иных классов с соответствующей сигнатурой можно рассматривать как объекты, хранимые в динамической памяти. В определенный момент происходит связывание ссылки и объекта (в этой роли выступают не обычные объекты, имеющие поля, а методы, задающие код). Взгляд на делегата как на указатель функции характерен для программистов, привыкших к C++.

Приведу теперь процедуру, тестирующую работу созданного класса:

public void TestOwnDel()

{

     int account = 1000, account1=0;

     OwnDel oda = new OwnDel('+');

     Console.WriteLine("account = {0}, account1 = {1}",

            account, account1);

     oda.pl(ref account); account1=oda.f1 (account);

     Console.WriteLine("account = {0}, accountl = {1}",

            account, account1);

}

Клиент класса OwnDel создает экземпляр класса, передавая конструктору знак той операции, которую он хотел бы выполнить над своими счетами — account и account1. Вызов p1 и f1, связанных к моменту вызова с закрытыми методами класса, приводит к выполнению нужных функций.

В нашем примере объявление экземпляров делегатов и связывание их с внутренними методами класса происходило в самом классе. Клиенту оставалось лишь вызывать уже созданные экземпляры, но эту работу можно выполнять и на стороне клиентского класса, чем мы сейчас и займемся. Рассмотрим многократно встречавшийся класс Person, слегка изменив его определение:

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)

    {

        int n=4;

        double I0=0, I1 = I(a, b, n,sif);

        for(n=8; n < Math.Pow(2.0,15.0); n*=2)

        {

           I0 =I1; I1=I(a,b,n,sif);

           if(Math.Abs(I1-10)<eps) break;

        }

        if(Math.Abs(I1–10)< eps)

        Console.WriteLine("Требуемая точность достигнута! "+

              " eps = {0}, достигнутая точность ={1}, n= {2}",

              eps,Math.Abs(11–10), n);

        else

            Console.WriteLine("Требуемая точность не достигнута!

1 ... 363 364 365 366 367 368 369 370 371 ... 415
Перейти на страницу:

Комментарии

Обратите внимание, что комментарий должен быть не короче 20 символов. Покажите уважение к себе и другим пользователям!

Никто еще не прокомментировал. Хотите быть первым, кто выскажется?