На этом шаге мы рассмотрим пример использования делегата, когда он является полем в классе.
Начиная с этого шага мы рассмотрим несколько примеров, которые иллюстрируют красоту и мощь делегатов. Начнем с примера, в котором экземпляр делегата (точнее, переменная, которая может ссылаться на экземпляр делегата) является полем в классе.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace pr169_1 { // Объявление делегата: delegate void MyDelegate(string txt); // Класс с полем, являющимся ссылкой на экземпляр делегата: class MyClass { // Поле является ссылкой на экземпляр делегата: public MyDelegate apply; // Конструктор: public MyClass(MyDelegate md) { apply = md; } } // Класс: class Alpha { // Закрытое текстовое поле: private string name; // Метод для присваивания значения полю: public void set(string t) { name = t; } // Переопределение метода ToString(): public override string ToString() { return name; } } // Класс с главным методом: class Program { // Главный метод: static void Main() { // Создание объекта: Alpha A = new Alpha(); // Создание объекта // (аргумент конструктора - ссылка на метод): MyClass obj = new MyClass(A.set); // Вызов экземпляра делегата: obj.apply("Объект A"); // Проверка поля объекта: Console.WriteLine(A); // Создание объекта: Alpha B = new Alpha(); // Полю значением присваивается ссылка на метод: obj.apply = B.set; // Вызов экземпляра делегата: obj.apply("Объект B"); // Проверка поля объекта: Console.WriteLine(B); // Добавление метода в список вызовов экземпляра // делегата: obj.apply += A.set; // Вызов экземпляра делегата: obj.apply("Объект X"); // Проверка полей объектов: Console.WriteLine(A + " и " + B); // Удаление метода из списка вызовов экземпляра // делегата: obj.apply -= B.set; // Вызов экземпляра делегата: obj.apply("Объект A"); // Проверка полей объектов: Console.WriteLine(A + " и " + B); // Задержка: Console.ReadLine(); } } }
Результат выполнения программы представлен ниже:

Рис.1. Результат выполнения программы
Делегат MyDelegate объявляется командой
delegate void MyDelegate(string txt); .
Экземпляры делегата смогут ссылаться на методы, имеющие один текстовый аргумент и не возвращающие результат. В классе MyClass описано поле apply, типом которого указан делегат MyDelegate. Это означает, что поле apply может ссылаться на метод (или список методов). Но самое важное - мы можем "вызывать" это поле (передав ему один текстовый аргумент). Также в классе описан конструктор с одним аргументом. Аргумент конструктора тоже примечательный - это переменная md типа MyDelegate. В теле конструктора командой
apply = md;
Если в качестве типа переменной указан делегат, то такая переменная, по своей сути, является ссылкой на метод (который соответствует параметрам делегата). Во всяком случае, значением переменной
может присваиваться ссылка на метод. Правда происходит все немного сложнее: когда переменной типа делегата присваивается ссылка на метод, создается экземпляр делегата, переменная ссылается
на этот экземпляр делегата, а экземпляр делегата ссылается на метод. Фактически экземпляр делегата является посредником между переменной и методом. Но в практическом плане, для понимания
происходящего, обычно можно отождествлять переменную типа делегата (как поле apply или аргумент конструктора класса MyClass) со ссылкой на метод.
Еще один класс Alpha имеет закрытое текстовое поле name. Для присваивания значения полю предусмотрен открытый метод set(), имеющий текстовый аргумент и не возвращающий результат. Для считывания значения поля name мы переопределяем метод ToString() (метод результатом возвращает значение поля name).
В методе Main() командой
Alpha A = new Alpha();
MyClass obj = new MyClass(A.set);
В несколько упрощенном виде общая последовательность действий такая. Аргумент конструктора класса MyClass объявлен как переменная типа MyDelegate. При вызове конструктора для
записи аргумента выделяется место в памяти - то есть создается техническая переменная типа MyDelegate. Аргументом конструктору передается ссылка A.set, которая и присваивается технической
переменной. В результате создается экземпляр делегата, который ссылается на метод set() объекта A, а техническая переменная (аргумент конструктора) ссылается на этот экземпляр. В
теле конструктора, при выполнении команды
apply = md; ,
Поэтому при выполнении команды
obj.apply("Объект A"); ,
Console.WriteLine(A); .
Затем командой
Alpha B = new Alpha();
obj.apply = B.set;
obj.apply("Объект B");
Console.WriteLine(B); .
Командой
obj.apply += A.set;
obj.apply("Объект X"); ,
Console.WriteLine(A + " и " + B); .
Наконец, выполнение команды
obj.apply -= B.set;
obj.apply("Объект A");
На следующем шаге мы продолжим изучение этого вопроса.