СДЕЛАЙТЕ СВОИ УРОКИ ЕЩЁ ЭФФЕКТИВНЕЕ, А ЖИЗНЬ СВОБОДНЕЕ

Благодаря готовым учебным материалам для работы в классе и дистанционно

Скидки до 50 % на комплекты
только до

Готовые ключевые этапы урока всегда будут у вас под рукой

Организационный момент

Проверка знаний

Объяснение материала

Закрепление изученного

Итоги урока

Применение делегатов

Категория: Информатика

Нажмите, чтобы узнать подробности

Просмотр содержимого документа
«Применение делегатов»

В прошлой теме подробно были рассмотрены делегаты. Однако данные примеры, возможно, не показывают истинной силы делегатов, так как нужные нам методы в данном случае мы можем вызвать и напрямую без всяких делегатов. Однако наиболее сильная сторона делегатов состоит в том, что они позволяют делегировать выполнение некоторому коду извне. И на момент написания программы мы можем не знать, что за код будет выполняться. Мы просто вызываем делегат. А какой метод будет непосредственно выполняться при вызове делегата, будет решаться потом. Например, наши классы будут распространяться в виде отдельной библиотеки классов, которая будет подключаться в проект другого разработчика. И этот разработчик захочет определить какую-то свою логику обработки, но изменить исходный код нашей библиотеки классов он не может. И делегаты как раз предоставляют возможность вызвать некое действие, которое задается извне и которое на момент написания кода может быть неизвестно.

Рассмотрим подробный пример. Пусть у нас есть класс, описывающий счет в банке:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

class Account

{

    int _sum; // Переменная для хранения суммы

 

    public Account(int sum)

    {

        _sum = sum;

    }

 

    public int CurrentSum

    {

        get { return _sum; }

    }

 

    public void Put(int sum)

    {

        _sum += sum;

    }

 

    public void Withdraw(int sum)

    {

        if (sum

        {

            _sum -= sum;

        }

    }

}

Допустим, в случае вывода денег с помощью метода Withdraw нам надо как-то уведомлять об этом самого клиента и, может быть, другие объекты. Для этого создадим делегат AccountStateHandler. Чтобы использовать делегат, нам надо создать переменную этого делегата, а затем присвоить ему метод, который будет вызываться делегатом.

Итак, добавим в класс Account следующие строки:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

class Account

{

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

    public delegate void AccountStateHandler(string message);

    // Создаем переменную делегата

    AccountStateHandler _del;

 

    // Регистрируем делегат

    public void RegisterHandler(AccountStateHandler del)

    {

        _del = del;

    }

     

    // Далее остальные строки класса Account

Здесь фактически проделываются те же шаги, что были выше, и есть практически все кроме вызова делегата. В данном случае у нас делегат принимает параметр типа string. Теперь изменим метод Withdraw следующим образом:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

public void Withdraw(int sum)

{

    if (sum

    {

         _sum -= sum;

 

        if (_del != null)

            _del($"Сумма {sum} снята со счета");

    }

    else

    {

        if (_del != null)

            _del("Недостаточно денег на счете");

    }

}

Теперь при снятии денег через метод Withdraw мы сначала проверяем, имеет ли делегат ссылку на какой-либо метод (иначе он имеет значение null). И если метод установлен, то вызываем его, передавая соответствующее сообщение в качестве параметра.

Теперь протестируем класс в основной программе:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

class Program

{

    static void Main(string[] args)

    {

        // создаем банковский счет

        Account account = new Account(200);

        // Добавляем в делегат ссылку на метод Show_Message

        // а сам делегат передается в качестве параметра метода RegisterHandler

        account.RegisterHandler(new Account.AccountStateHandler(Show_Message));

        // Два раза подряд пытаемся снять деньги

        account.Withdraw(100);

        account.Withdraw(150);

        Console.ReadLine();

    }

    private static void Show_Message(String message)

    {

        Console.WriteLine(message);

    }

Запустив программу, мы получим два разных сообщения:

Сумма 100 снята со счета

Недостаточно денег на счете

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

Опять же может возникнуть вопрос: почему бы в коде метода Withdraw() не выводить сообщение о снятии денег? Зачем нужно задействовать какой-то делегат?

Дело в том, что не всегда у нас есть доступ к коду классов. Например, часть классов может создаваться и компилироваться одним человеком, который не будет знать, как эти классы будут использоваться. А использовать эти классы будет другой разработчик.

Так, здесь мы выводим сообщение на консоль. Однако для класса Account не важно, как это сообщение выводится. Классу Account даже не известно, что вообще будет делаться в результате списания денег. Он просто посылает уведомление об этом через делегат.

В результате, если мы создаем консольное приложение, мы можем через делегат выводить сообщение на консоль. Если мы создаем графическое приложение Windows Forms или WPF, то можно выводить сообщение в виде графического окна. А можно не просто выводить сообщение. А, например, записать при списании информацию об этом действии в файл или отправить уведомление на электронную почту. В общем любыми способами обработать вызов делегата. И способ обработки не будет зависеть от класса Account.

Хотя в примере наш делегат принимал адрес на один метод, в действительности он может указывать сразу на несколько методов. Кроме того, при необходимости мы можем удалить ссылки на адреса определенных методов, чтобы они не вызывались при вызове делегата. Итак, изменим в классе Account метод RegisterHandler и добавим новый метод UnregisterHandler, который будет удалять методы из списка методов делегата:

1

2

3

4

5

6

7

8

9

10

// Регистрируем делегат

public void RegisterHandler(AccountStateHandler del)

{

    _del += del; // добавляем делегат

}

// Отмена регистрации делегата

public void UnregisterHandler(AccountStateHandler del)

{

    _del -= del; // удаляем делегат

}

В первом методе объединяет делегаты _del и del в один, который потом присваивается переменной _del. Во втором методе удаляется делегат del. Теперь перейдем к основной программе:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

class Program

{

    static void Main(string[] args)

    {

        Account account = new Account(200);

        Account.AccountStateHandler colorDelegate = new Account.AccountStateHandler(Color_Message);

 

        // Добавляем в делегат ссылку на методы

        account.RegisterHandler(new Account.AccountStateHandler(Show_Message));

        account.RegisterHandler(colorDelegate);

        // Два раза подряд пытаемся снять деньги

        account.Withdraw(100);

        account.Withdraw(150);

 

        // Удаляем делегат

        account.UnregisterHandler(colorDelegate);

        account.Withdraw(50);

             

        Console.ReadLine();

    }

    private static void Show_Message(String message)

    {

        Console.WriteLine(message);

    }

    private static void Color_Message(string message)

    {

        // Устанавливаем красный цвет символов

        Console.ForegroundColor = ConsoleColor.Red;

        Console.WriteLine(message);

        // Сбрасываем настройки цвета

        Console.ResetColor();

    }

В целях тестирования мы создали еще один метод - Color_Message, который выводит то же самое сообщение только красным цветом. Для первого делегата создается отдельная переменная. Но большой разницы между передачей обоих в метод account.RegisterHandler нет: просто в одном случае мы сразу передаем объект, создаваемый конструктором account.RegisterHandler(new Account.AccountStateHandler(Show_Message));

Во втором случае создаем переменную и ее уже передаем в метод account.RegisterHandler(colorDelegate);.

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

Сумма 100 снята со счета

Сумма 100 снята со счета

Недостаточно денег на счете

Недостаточно денег на счете

Сумма 50 снята со счета