Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
[ТП]Lektsii / Лекция 6 Обработка событий.doc
Скачиваний:
85
Добавлен:
31.05.2015
Размер:
159.23 Кб
Скачать

Лекция: Делегаты. Определение и обработка событий Функциональный тип в c#. Делегаты

Делегаты в C# позволяют применять методы одного класса к объектам другого класса, который может эти методы вызвать. Вы можете применить метод m класса A, определенного в делегате, к классу B, который будет способен вызвать метод m класса A. Вы можете вызвать как статические методы, так и нестатические. Эта концепция хорошо известна разработчикам C++, которые использовали указатель на функцию в качестве параметра, передаваемого другой функции. Концепция делегатов была введена в Visual J++, а затем перенесена в C#. Делегаты C# являются в .NET Framework реализацией в качестве производного класса от System.Delegate. Использование делегатов можно описать четырьмя шагами.

  1. Определение объекта делегата с сигнатурой, точно соответствующей сигнатуре метода, который пытаемся связать.

  2. Определение метода с сигнатурой делегата, определенного на первом шаге

  3. Создание объекта делегата и связывание их с методами

  4. Вызов связанного метода с помощью объекта делегата

Слово делегат (delegate) используется в C# для обозначения хорошо известного понятия. Делегат задает определение функционального типа (класса) данных. Экземплярами класса являются функции. Описание делегата в языке C# представляет собой описание еще одного частного случая класса. Каждый делегат описывает множество функций с заданной сигнатурой. Каждая функция (метод), сигнатура которого совпадает с сигнатурой делегата, может рассматриваться как экземпляр класса, заданного делегатом. Синтаксис объявления делегата имеет следующий вид:

[<спецификатор доступа>] delegate <тип результата > <имя класса> (<список аргументов>);

Этим объявлением класса задается функциональный тип - множество функций с заданной сигнатурой, у которых аргументы определяются списком, заданным в объявлении делегата, и тип возвращаемого значения определяется типом результата делегата.

Спецификатор доступа может быть, как обычно, опущен. Где следует размещать объявление делегата? Как и у всякого класса, есть две возможности:

  1. • непосредственно в пространстве имен, наряду с объявлениями других классов, структур, интерфейсов;

  2. • внутри другого класса, наряду с объявлениями методов и свойств. Такое объявление рассматривается как объявление вложенного класса.

Так же, как и интерфейсы C#, делегаты не задают реализации. Фактически между некоторыми классами и делегатом заключается контракт на реализацию делегата. Классы, согласные с контрактом, должны объявить у себя статические или динамические функции, сигнатура которых совпадает с сигнатурой делегата. Если контракт выполняется, то можно создать экземпляры делегата, присвоив им в качестве значений функции, удовлетворяющие контракту. Заметьте, контракт является жестким: не допускается ситуация, при которой у делегата тип параметра - object, а у экземпляра соответствующий параметр имеет тип, согласованный сobject, например,int.

Начнем примеры этой лекции с объявления трех делегатов. Поместив два из них в пространство имен, третий вложим непосредственно в создаваемый нами класс:

namespace Delegates

{

//объявление классов - делегатов

delegate void Proc(ref int x);

delegate void MesToPers(string s);

class OwnDel

{

public delegate int Fun1(int x);

int Plus1( int x){return(x+100);}//Plus1

int Minus1(int x){return(x-100);}//Minus1

void Plus(ref int x){x+= 100;}

void Minus(ref int x){x-=100;}

//поля класса

public Proc p1;

public Fun1 f1;

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

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

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

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

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

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

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

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

public void TestOwnDel()

{

int account = 1000, account1=0;

OwnDel oda = new OwnDel('+');

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

account, account1);

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

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

account, account1);

}

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

Еще пример. Следующий C# код показывает вышеупомянутые шаги в реализации одного делегата и четырех классов.

using System;

// Шаг 1. Определение делегата с сигнатурой связываемого метода

public delegate void MyDelegate(string input);

// Шаг 2. Определение метода с сигнатурой определенного делегата

class MyClass1{

public void delegateMethod1(string input){

Console.WriteLine(

"This is delegateMethod1 and the input to the method is {0}",

input);

}

public void delegateMethod2(string input){

Console.WriteLine(

"This is delegateMethod2 and the input to the method is {0}",

input);

}

}

// Шаг 3. Создание объектов делегата и связывание с методами

class MyClass2{

public MyDelegate createDelegate(){

MyClass1 c2=new MyClass1();

MyDelegate d1 = new MyDelegate(c2.delegateMethod1);

MyDelegate d2 = new MyDelegate(c2.delegateMethod2);

MyDelegate d3 = d1 + d2;

return d3;

}

}

// Шаг 4. Вызов метода с помощью делегата

class MyClass3{

public void callDelegate(MyDelegate d,string input){

d(input);

}

}

class Driver{

static void Main(string[] args){

MyClass2 c2 = new MyClass2();

MyDelegate d = c2.createDelegate();

MyClass3 c3 = new MyClass3();

c3.callDelegate(d,"Calling the delegate");

}

}

Еще пример: