Опубликован: 25.03.2010 | Уровень: для всех | Доступ: платный
Лекция 10:

Интерфейсы, делегаты, события в C#

Методы Combine() и Remove()

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

MyDelegate del;
            del = new MyDelegate(obj.Handler1);
            del += new MyDelegate(obj.Handler2);
    
            // Или
            del = new MyDelegate(obj.Handler1);
            del = (MyDelegate)Delegate.Combine(del, 
              new MyDelegate(obj.Handler2));
using System;
    
namespace Test
{
    class Handler
    {
        // Функции
        public void Handler1()
        {
            Console.WriteLine("Вызов Handler1()");
        }
    
        public void Handler2()
        {
            Console.WriteLine("Вызов Handler2()");
        }
    }
    
    // Вызывающая сторона
    class MyClass
    {
        public static string Title = "Применение Combine() и Remove()";
    
        // Объявляем делегат как член класса
        delegate void MyDelegate();
    
        public MyClass()
        {
            // Создаем объект
            Handler obj=new Handler();
    
            // Формируем список объекта-делегата из 2 вызовов
            MyDelegate del1 = new MyDelegate(obj.Handler1);
            del1 += new MyDelegate(obj.Handler1);
    
            // Еще один делегат того же типа из 3 вызовов
            MyDelegate del2 = new MyDelegate(obj.Handler2);
            del2 += obj.Handler2;   // Упрощенный синтаксис
            del2 = del2 + obj.Handler2; // То же самое
    
            // Новый делегат из 5 вызовов 
            MyDelegate del3 = (MyDelegate)Delegate.Combine(del1, del2);
    
            // Вызываем 5 функций
            del3();
            Console.WriteLine();
    
            // Вновь формируем делегаты
            del1 = new MyDelegate(obj.Handler1);
            del1 += obj.Handler2;
    
            // Усекаем первый список вторым 
            del2 = (MyDelegate)Delegate.Remove(del3, del1);
    
            // Вызываем оставшиеся 3 функции
            del2();
        }
    }
    
    // Запуск
    class Program
    {
        static void Main()
        {
            // Настройка консоли
            Console.Title = MyClass.Title;
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
            Console.WindowWidth = 33;
            Console.WindowHeight = 10;
    
            new MyClass();// Исполняем
    
            Console.ReadLine();
        }
    }
}
Листинг 10.16 . Применение методов Combine() и Remove()

События в C#

Рассылка сообщений с помощью делегата

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

  1. список вызываемых функций,
  2. список обработчиков,
  3. список адресатов,
  4. список получателей и т.д.

Напомним, что если список адресатов пуст, то самого объекта-делегата не существует и ссылка на него имеет значение null. Без проверки этого обстоятельства при попытке вызова адресатов пустой ссылкой-делегатом будет сгенерировано стандартное исключение NullReferenceException.

Вот пример однонаправленной рассылки сообщения

using System;
    
namespace Test
{
    // Образец сообщения определяется делегатом
    delegate void Message(string message);
    
    // Источник сообщения
    class SourceMessage
    {
        // Общедоступное поле ссылки на объект-делегат,
        // который наполнится указателями
        // на функции в классах-получателях 
        public Message mail;
    
        // Необязательное поле с рассылаемым сообщением
        public string message;
    
        // Разослать сообщение - функция диспетчеризации
        public void DispatchMessage(string mess)
        {
            // Сохраняем внешнее сообщение во внутреннем поле
            message = mess;
    
            // Инициируем рассылку сообщения всем, 
            // кто зарегистрировался в объекте-делегате
            if (mail != null)   // Если не пустой делегат
                mail(mess);
        }
    }
    
    // Получатель сообщения
    class Addressee1
    {
        // Функции
        public void Handler(string message)
        {
            Console.WriteLine("Addressee1 получил:"
                + "\n\t\"{0}\"", message);
        }
    }
    
    // Получатель сообщения
    class Addressee2
    {
        // Функции
        public void Handler(string message)
        {
            Console.WriteLine("Addressee2 получил:"
                + "\n\t\"{0}\"", message);
        }
    }
    
    // Вызывающая сторона
    class MyClass
    {
        static public string Title = "Рассылка 
    сообщений делегатом";
    
        public MyClass()
        {
            // Создаем объекты источника и получателей сообщения
            SourceMessage source = new SourceMessage();
            Addressee1 obj1 = new Addressee1();
            Addressee2 obj2 = new Addressee2();
    
            // Формируем список вызовов объекта-делегата
            source.mail += new Message(obj1.Handler);
            source.mail += new Message(obj2.Handler);
    
            // Рассылаем сообщение напрямую через делегат
            source.mail("Первое сообщение");
            Console.WriteLine();
    
            // Рассылаем сообщение через функцию диспетчеризации
            source.DispatchMessage("Второе сообщение");
        }
    }
    
    // Запуск
    class Program
    {
        static void Main()
        {
            // Настройка консоли
            Console.Title = MyClass.Title;
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
            Console.WindowWidth = 32;
            Console.WindowHeight = 10;
    
            new MyClass();// Исполняем
    
            Console.ReadLine();
        }
    }
}
Листинг 10.17 . Однонаправленная передача сообщений объектам с помощью делегата

Обратите внимание, что при добавлении в делегат первой ссылки на функцию нам не обязательно использовать операцию ' = ', а можно и ' += ', несмотря на то, что делегат еще пустой.


В классе-источнике сообщения SourceMessage мы объявили два общедоступных поля: ссылку mail на объект-делегат и переменную message для хранения сообщения внутри класса. Поле-переменная message для работы программы не нужна, но мы ее ввели просто для того, чтобы сравнить представления в панели Class View.


Если теперь посмотреть на состав класса SourceMessage через панель Class View, то мы не сможем различить две этих переменные - обычные поля, да и только. Но ведь поле mail не выполняет роль хранителя ссылки на обычный экземпляр класса или структуры, а предназначено для создания механизма адресации функций.

Максим Филатов
Максим Филатов

Прошел курс. Получил код Dreamspark. Ввожу код на сайте, пишет:

Срок действия этого кода проверки уже истек. Проверьте, правильно ли введен код. У вас осталось две попытки. Вы также можете выбрать другой способ проверки или предоставить соответствующие документы, подтверждающие ваш академический статус.

 

Как активировать код?

Денис Пашков
Денис Пашков
Россия
Татьяна Ковалюк
Татьяна Ковалюк
Украина, Киев, Киевский политехнический институт, 1974