Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
лр1_Делегаты.doc
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
187 Кб
Скачать
☆

2.2. Одиночные делегаты

Поскольку CLR объектно-ориентированная среда, в ней все так или иначе связано с классами и объектами. В полной мере это относится и к делегатам. Делегаты в CLR реализованы в виде классов, производных от базового класса делегата: System.Delegate или System.MulticastDelegate. Строго говоря, делегат – это класс, содержащий данные о сигнатуре метода. Экземпляр делегата (delegae instance) – объект, который позволяет привязаться к конкретному методу, соответствующему определенной сигнатуре (подобно объявлению указателя на функцию в C/C++). Как и указатель на функцию делегат может быть передан в виде аргумента.

Рассмотрим действия, которые надо проделать, чтобы воспользоваться делегатами. Для начала необходимо объявить делегат. Для объявления делегатов в C Sharp используется ключевое слово delegate. То есть концепция делегатов непосредственно поддерживается синтаксисом языка. Ниже приведен пример объявления делегата:

delegate string MyDelegate(int x);

Все что мы получили от вышеприведенного объявления – это новый тип (класс). Это и следовало было ожидать, так как класс и является пользовательским типом данных. Что определяет этот класс? Данный класс определяет форму метода – его сигнатуру. В сигнатуру метода входит тип возвращаемого значения и список аргументов. В нашем случае, метод должен возвращать строку и принимать один целочисленный аргумент. Так как делегат является классом, он может быть объявлен как в пространстве имен, так и в классе. Оба ниже приведенных листинга содержат корректные объявления:

//листинг 1

using System;

delegate string MyDelegate(int x);

class TestApp

{

            public static void Main() {  …  }

}

//листинг 2

using System;

class TestApp

{

            delegate string MyDelegate(int x);

            public static void Main() { …  }

}

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

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

MyDelegate dlg;   //объявление экземпляра делегата

В этом фрагменте кода MyDelegate – имя класса, а dlg – имя объекта. Как и любой объект в C#, перед использованием экземпляр делегата необходимо инициализировать. И в этом случае экземпляр делегата инициализируется точно так же, как и экземпляр любого другого класса, при помощи оператора new.

MyDelegate dlg;                              //объявление экземпляра делегата

dlg = new MyDelegate(MethodName);  //инициализация экземпляра делегата

Однако здесь есть одно очень существенное отличие. Как видно из вышеприведенного фрагмента кода, конструктору делегата передается идентификатор метода (имя метода), на который будет указывать экземпляр делегата. Отличие заключается в том, что при попытке передать идентификатор метода конструктору обычного класса, независимо от типа аргумента конструктора, будет выдано следующее сообщение об ошибке:

de.cs(31,37): error CS0654: Method 'TestApp.MyMethod(int)' referenced without an argument list

Данный факт объясняется тем, что концепция делегатов поддерживается синтаксисом C#. Передача имени метода допустима только конструкторам делегатов. Как и для обычных классов, при объявлении экземпляр делегата можно инициализировать:

// объявление и инициализация экземпляра делегата

MyDelegate dlg = new MyDelegate(MethodName);

Экземпляр делегата может быть привязан, как к статическому, так и к нестатическому методу класса. В случае со статическим методом, перед именем метода указывается имя класса. Если имя класса опущено, подразумевается класс, в реализации которого встречается инициализация экземпляра делегата. В случае с нестатическим методом, перед его именем указывается имя объекта. Если имя объекта опущено, то предполагается, что используется текущий экземпляр (this):

dlg = new MyDelegate(ClassName.MethodName);     //статический метод

dlg = new MyDelegate(ObjectName.MethodName);   //нестатический метод

Экземпляр делегата объявлен и инициализирован, все готово для его использования. Вызов метода, на который указывает делегат, напоминает вызов обычного метода, но в этом случае вместо имени метода указывается имя экземпляра делегата: dlg(55);

В круглых скобках указывается список аргументов. И естественно, поскольку наш метод возвращает строковое значение, мы можем получить его:

sting str = dlg(55);

А теперь рассмотрим простой пример использования делегата:

// Первый вариант

using System;

// Объявляем делегат

delegate string MyDelegate(int arg);

class TestApp

{

            //статический метод

            public static string MyMethod(int arg) {  return arg.ToString();  }

            public static void Main()

{

                        //создаем экземпляр делегата и инициализируем его

                        MyDelegate dlg = new MyDelegate(TestApp.MyMethod);

                        //вызываем метод, на который указывает делегат

                        string returnValue = dlg(25);

                        Console.WriteLine(returnValue); //выводим полученное значение

Console.ReadLine(returnValue); / ожидание нажатия на любую клавишу

            }

}

Рассмотрим еще один (более сложный) пример с использованием класса Person:

// Второй вариант

using System;

// Объявляем делегат

delegate void MesToPers(string s);

class Person

{

// Данные

private string message;

// Данные для хранения значений свойств:

private string name;

private int id;

private double salary;

// Свойства:

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;}

}

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

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;}

// Mетоды

public void ToPerson(string mes)

{

this.message = mes;

Console.WriteLine("{0}, {1}",name, message);

}

}

Класс Person устроен обычным способом: у него несколько перегруженных конструкторов, закрытые поля и процедуры-свойства для доступа к ним. Особо обратить внимание следует на метод класса ToPerson, сигнатура которого совпадает с сигнатурой класса, определенной делегатом MesToPers. Посмотрите, как клиент класса может связать этот метод с экземпляром делегата, определенного самим клиентом:

class TestApp

{

public static void Main()

{

// объявляем переменную типа Person и создаем ее экземпляр

Person man1 = new Person("Владимир");

// объявляем переменную personMessag типа делегата, создаем его экземпляр и

// связываем его с методом объекта man1 класса Person

MesToPers personMessage = new MesToPers(man1.ToPerson);

// Через делегата передаем сообщение объекту man1

personMessage("пора работать!");

// Ждем нажатия на любую клавишу

Console.ReadKey();

}

}

Обратите внимание, что поскольку метод ToPerson не является статическим методом, то при связывании делегата с методом необходимо передать и объект, вызывающий этот метод. Более того, переданный объект становится доступным экземпляру делегата. Отсюда становится ясно, что экземпляры делегата - это не просто указатели на функцию, а более сложно организованные структуры. Они, по крайней мере, содержат пару указателей на метод и на объект, вызвавший метод. Вызываемый метод в своей работе использует как информацию, передаваемую ему через аргументы метода, так и информацию, хранящуюся в полях объекта. В данном примере переданное сообщение "пора работать" присоединится к имени объекта, и результирующая строка будет выдана на печать. В тех случаях, когда метод, связываемый с экземпляром делегата, не использует информацию объекта, этот метод может быть объявлен как статический метод класса. Таким образом, инициализировать экземпляры делегата можно как статическими, так и динамическими методами, связанными с конкретными объектами.