Рейтинговые книги
Читем онлайн Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 152 153 154 155 156 157 158 159 160 ... 407
class="code">Console.WriteLine("10 + 10 is {0}", b(10, 10));

Console.ReadLine();

В данном случае вывод будет выглядеть следующим образом:

***** Simple Delegate Example *****

Method Name: Int32 Add(Int32, Int32)

Type Name: SimpleDelegate.SimpleMath

10 + 10 is 20

Отправка уведомлений о состоянии объекта с использованием делегатов

Очевидно, что предыдущий пример SimpleDelegate был чисто иллюстративным по своей природе, т.к. нет особых причин создавать делегат просто для того, чтобы сложить два числа. Рассмотрим более реалистичный пример, в котором делегаты применяются для определения класса Car, обладающего способностью информировать внешние сущности о текущем состоянии двигателя. В таком случае нужно выполнить перечисленные ниже действия.

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

2. Объявить переменную-член этого типа делегата в классе Car.

3. Создать в классе Car вспомогательную функцию, которая позволяет вызывающему коду указывать метод для обратного вызова.

4. Реализовать метод Accelerate() для обращения к списку вызовов делегата в подходящих обстоятельствах.

Для начала создайте новый проект консольного приложения по имени CarDelegate. Определите в нем новый класс Car, начальный код которого показан ниже:

using System;

using System.Linq;

namespace CarDelegate

{

  public class Car

  {

    // Внутренние данные состояния.

    public int CurrentSpeed { get; set; }

    public int MaxSpeed { get; set; } = 100;

    public string PetName { get; set; }

    // Исправен ли автомобиль?

    private bool _carIsDead;

    // Конструкторы класса.

    public Car() {}

    public Car(string name, int maxSp, int currSp)

  {

      CurrentSpeed = currSp;

      MaxSpeed = maxSp;

      PetName = name;

    }

  }

}

А теперь модифицируйте его, выполнив первые три действия из числа указанных выше:

public class Car

{

  ...

  // 1. Определить тип делегата.

  public delegate void CarEngineHandler(string msgForCaller);

  // 2. Определить переменную-член этого типа делегата.

  private CarEngineHandler _listOfHandlers;

  // 3. Добавить регистрационную функцию для вызывающего кода.

  public void RegisterWithCarEngine(CarEngineHandler methodToCall)

  {

    _listOfHandlers = methodToCall;

  }

}

В приведенном примере обратите внимание на то, что типы делегатов определяются прямо внутри области действия класса Car; безусловно, это необязательно, но помогает закрепить идею о том, что делегат естественным образом работает с таким отдельным классом. Тип делегата CarEngineHandler может указывать на любой метод, который принимает значение string как параметр и имеет void в качестве возвращаемого типа.

Кроме того, была объявлена закрытая переменная-член делегата (_listOfHandlers) и вспомогательная функция (RegisterWithCarEngine()), которая позволяет вызывающему коду добавлять метод в список вызовов делегата.

На заметку! Строго говоря, переменную-член типа делегата можно было бы определить как public, избежав тем самым необходимости в создании дополнительных методов регистрации. Тем не менее, за счет определения этой переменной-члена типа делегата как private усиливается инкапсуляция и обеспечивается решение, более безопасное в отношении типов. Позже в главе при рассмотрении ключевого слова event языка C# мы еще вернемся к анализу рисков объявления переменных-членов с типами делегатов как public.

Теперь необходимо создать метод Accelerate(). Вспомните, что цель в том, чтобы позволить объекту Car отправлять связанные с двигателем сообщения любому подписавшемуся прослушивателю. Вот необходимое обновление:

// 4. Реализовать метод Accelerate() для обращения к списку

//    вызовов делегата в подходящих обстоятельствах.

public void Accelerate(int delta)

{

  /// Если этот автомобиль сломан, то отправить сообщение об этом.

  if (_carIsDead)

   {

    _listOfHandlers?.Invoke("Sorry, this car is dead...");

  }

  else

  {

    CurrentSpeed += delta;

    // Автомобиль почти сломан?

    if (10 == (MaxSpeed - CurrentSpeed))

    {

      _listOfHandlers?.Invoke("Careful buddy! Gonna blow!");

    }

    if (CurrentSpeed >= MaxSpeed)

    {

      _carIsDead = true;

    }

    else

    {

      Console.WriteLine("CurrentSpeed = {0}", CurrentSpeed);

    }

  }

}

Обратите внимание, что при попытке вызова методов, поддерживаемых переменной-членом _listOfHandlers, используется синтаксис распространения null. Причина в том, что создание таких объектов посредством вызова вспомогательного метода RegisterWithCarEngine() является задачей вызывающего кода. Если вызывающий код не вызывал RegisterWithCarEngine(), а мы попытаемся обратиться к списку вызовов делегата, то получим исключение NullReferenceException во время выполнения. Теперь, когда инфраструктура делегатов готова, внесите в файл Program.cs следующие изменения:

using System;

using CarDelegate;

Console.WriteLine("** Delegates as event enablers **n");

// Создать объект Car.

Car c1 = new Car("SlugBug", 100, 10);

// Сообщить объекту Car, какой метод вызывать,

// когда он пожелает отправить сообщение.

c1.RegisterWithCarEngine(

  new Car.CarEngineHandler(OnCarEngineEvent));

// Увеличить скорость (это инициирует события).

Console.WriteLine("***** Speeding up *****");

for (int i = 0; i < 6; i++)

{

  c1.Accelerate(20);

}

Console.ReadLine();

// Цель для входящих сообщений.

static void OnCarEngineEvent(string msg)

{

  Console.WriteLine("n*** Message From Car Object ***");

  Console.WriteLine("=> {0}", msg);

  Console.WriteLine("********************n");

}

Код начинается с создания нового объекта Car. Поскольку вас интересуют события, связанные с двигателем, следующий шаг заключается в вызове специальной регистрационной функции RegisterWithCarEngine(). Вспомните, что метод RegisterWithCarEngine() ожидает получения экземпляра вложенного делегата CarEngineHandler, и как в случае любого делегата, в параметре конструктора передается метод, на который он должен указывать. Трюк здесь в том, что интересующий метод находится в классе Program! Обратите также внимание, что метод OnCarEngineEvent() полностью соответствует связанному делегату, потому что принимает string и возвращает void. Ниже показан вывод приведенного примера:

***** Delegates as event enablers *****

***** Speeding up *****

CurrentSpeed = 30

CurrentSpeed = 50

CurrentSpeed = 70

***** Message From

1 ... 152 153 154 155 156 157 158 159 160 ... 407
На этой странице вы можете бесплатно читать книгу Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен бесплатно.

Оставить комментарий