Делегаты

В языке С и С++ в программах широко используют указатели. И указатели на объекты данных, и указатели на функции. Это повышает универсальность программы, позволяет решать самые сложные задачи, например: моделировать работу любых динамических структур данных.

Другое дело — язык язык С#. В нём не принято напрямую работать с указателями. Для повышения безопасности программ в этом языке созданы классы, позволяющие моделировать работу указателей. Так, для работы с динамическими структурами данных в С# используют коллекции. А вот вместо указателей на функции разработчики .NET придумали понятие делегата.

Делегат — это особый вид класса. Создав объект делегата, можно инкапсулировать в него указатель на нужный метод. Инкапсуляция в делегат указателя на метод делает такой указатель безопасным, так как управляемый объект-делегат контролируется системой.

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

Рассмотрим основные действия при работе с делегатами.

1)Объявление делегата

Прежде чем создать объект любого собственного класса, требуется объявить этот класс. В случае с делегатами это делается так:

delegate тип_возвращаемого_значения тип_делегата (список_формальных_параметров);

где

Если делегат — это класс, то возникает законный вопрос: а где же определение этого класса, т.е. тело класса? В формальной записи, приведённой выше, есть только заголовок этого класса, заканчивающийся точкой с запятой, но тело отсутствует.

Ответ простой: программисту не нужно вообще в тексте своей программы определять тело делегата! Компилятор сам создаёт реализацию делегата, используя описание делегата. Это очень удобно для пользователя, и было, по-видимому, не слишком сложно для разработчиков платформы .NET, ведь действия с указателями на функции однотипны.

2)Создание ссылки на делегат данного типа

Выполняется следующим образом:

тип_делегата ссылка_на_делегат;

где

3.Создание объекта-делегата заданного типа

И здесь всё достаточно просто, но есть два варианта реализации:

а)создание объекта-делегата, инкапсулирующего статическую функцию:

ссылка_на_делегат = new тип_делегата(имя_класса.имя_статической_функции);

б)создание объекта-делегата, инкапсулирующего обычную функцию:

ссылка_на_делегат = new тип_делегата(ссылка_на_объект.имя_обычной_функции);

Внимание! Описание функций, которые мы инкапсулируем в делегат, должно строго соответствовать тем требованиям, которые были указаны при объявлении делегата, т.е. тип результата и список параметров у функций, указанный в обоих случаях (статическая или обычная), обязательно должен соответствовать типу результата и списку параметров типа делегата.

Понятно, что шаги 2 и 3 можно совместить.

4.Применение объекта-делегата

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

ссылка_на_делегат(фактические_параметры);

Другие способах применения делегатов рассмотрим позже, а пока разберём простейший пример.

Пример 1. Рассмотрим работу делегатов с обычными и статическими методами, а также создание множественного делегата.

Текст программы:

using System;

namespace Prim_Delegate_01

{

   public delegate void Del(string s);

   class Program

   {

      static void f_stat(string s)

      {

         Console.WriteLine("Статическая: {0}",s);

      }

      void f(string s)

      {

         Console.WriteLine("Обычная: {0}",s);

      }

      public static void Main(string[] args)

      {

         Console.WriteLine("Использование делегатов\n");

         Program p = new Program();

         Del d1 = new Del(p.f);

         Del d2 = new Del(Program.f_stat);

         d1("обычная");

         d2("статическая");

         d1+=new Del(Program.f_stat);

         d1("обычная и статическая");

         d1-=d2;

         d1("обычная");

         Console.Write("\nPress any key to continue . . . ");

         Console.ReadKey(true);

         }

   }

}


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

Использование делегатов


Обычная: обычная

Статическая: статическая

Обычная: обычная и статическая

Статическая: обычная и статическая

Обычная: обычная


Press any key to continue . . .


Пример 2. Предположим, что необходимо написать программу для вычисления определённого интеграла , например, по методу трапеций. Реализацию численного метода вычисления интеграла и вычисление подынтегральной функции логично оформить в виде отдельных подпрограмм (в терминах языка C# — в виде методов). Для повышения универсальности было бы хорошо передавать имя подынтегральной функции в функцию, реализующую метод интегрирования — в Integral(), чтобы там использовать некоторое формальное имя (хотя бы f()). Для этого будем использовать делегат.

Текст программы:

using System;

namespace Prim_delegate_Integral

{

   public delegate double del(double x);

   class C1

   {

      public static double f(double x)

      {

         return x*x;

      }

   }

   class Program

   {

      public static double Integral(del f, double a, double b, int n)

      {

         double x, s=(f(a) + f(b)) / 2 , h = (b - a) / n;

         for(int i = 1; i < n; i++)

         {

            x = a + i * h;

            s += f(x);

         }

         s *= h;

         return s;

      }

      public static void Main(string[] args)

      {

         Console.WriteLine("Передача имени метода в подпрограмму:\n");

         del d = new del(C1.f);

         double a=0, b=1, s;

         int n=100;

         s=Integral(d,a,b,n);

         Console.WriteLine("s={0}",s);

         Console.Write("\nPress any key to continue . . . ");

         Console.ReadKey(true);

      }

   }

}



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

Передача имени метода в подпрограмму:


s=0,33335


Press any key to continue . . .



Назад
На верх
Вперёд
Hosted by uCoz