Опубликован: 15.09.2010 | Доступ: свободный | Студентов: 4806 / 630 | Оценка: 3.97 / 3.80 | Длительность: 14:45:00
Лекция 10:

Делегаты и события

< Лекция 9 || Лекция 10: 1234

Операции

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

С делегатами одного типа можно выполнять операции простого и сложного присваивания, например:

Del d1 = new Del( o1.Do );      // o1.Do
Del d2 = new Del( o2.Do );      // o2.Do
Del d3 = d1 + d2;               // o1.Do и o2.Do
d3 += d1;                       // o1.Do, o2.Do и o1.Do
d3 -= d2;                       // o1.Do и o1.Do

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

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

Передача делегатов в методы

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

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

using System;
namespace ConsoleApplication1
{
    public delegate double Fun( double x );           // объявление делегата

    class Class1
    {
        public static void Table( Fun F, double x, double b )
        {
            Console.WriteLine( " ----- X ----- Y -----" );
            while (x <= b)
            {
               Console.WriteLine( "| {0,8:0.000} | {1,8:0.000} |", x, F(x));
               x += 1;
            }
            Console.WriteLine( " ---------------------" );
        }

        public static double Simple( double x )
        {
            return 1;
        }
        
        static void Main()
        {
            Console.WriteLine( " Таблица функции Sin " );
            Table( new Fun( Math.Sin ), -2, 2 );

            Console.WriteLine( " Таблица функции Simple " );
            Table( new Fun( Simple ), 0, 3 );
        }
    }
}
Листинг 10.3. Передача делегата через список параметров

Результат работы программы:

Таблица функции Sin
 ----- X ----- Y -----
|   -2,000 |   -0,909 |
|   -1,000 |   -0,841 |
|    0,000 |    0,000 |
|    1,000 |    0,841 |
|    2,000 |    0,909 |
 ---------------------
 Таблица функции Simple
 ----- X ----- Y -----
|    0,000 |    1,000 |
|    1,000 |    1,000 |
|    2,000 |    1,000 |
|    3,000 |    1,000 |
 ---------------------

Обратный вызов (callback) представляет собой вызов функции, передаваемой в другую функцию в качестве параметра. Рассмотрим рисунок 10.1. Допустим, в библиотеке описана функция А, параметром которой является имя другой функции. В вызывающем коде описывается функция с требуемой сигнатурой ( В ) и передается в функцию А. Выполнение функции А приводит к вызову В, то есть управление передается из библиотечной функции обратно в вызывающий код.

Механизм обратного вызова

Рис. 10.1. Механизм обратного вызова

Механизм обратного вызова широко используется в программировании. Например, он реализуется во многих стандартных функциях Windows.

Начиная с Visual Studio 2005, использующей версию 2.0 языка C#, можно применять упрощенный синтаксис для делегатов. Первое упрощение заключается в том, что в большинстве случаев явным образом создавать экземпляр делегата не требуется, поскольку он создается автоматически по контексту. Второе упрощение заключается в возможности создания так называемых анонимных методов — фрагментов кода, описываемых непосредственно в том месте, где используется делегат:

static void Main()
        {
            Console.WriteLine( " Таблица функции Sin " );
            Table( Math.Sin, -2, 2 );                         // упрощение 1

            Console.WriteLine( " Таблица функции Simple " );
            Table( delegate (double x ){ return 1; }, 0, 3 ); // упрощение 2
        }
    }
}

В первом случае экземпляр делегата, соответствующего функции Sin, создается автоматически. Чтобы это могло произойти, список параметров и тип возвращаемого значения функции должны быть совместимы с делегатом. Во втором случае не требуется оформлять простой фрагмент кода в виде отдельной функции Simple, как это было сделано в предыдущем листинге, — код функции оформляется как анонимный метод и встраивается прямо в место передачи.

< Лекция 9 || Лекция 10: 1234
Stefan Berzan
Stefan Berzan
Молдова, Кишинев
Дмитрий Казимиров
Дмитрий Казимиров
Россия, Омск