Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

Очевидно, что предыдущий пример

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(

Поделиться с друзьями: