Язык программирования C#9 и платформа .NET5
using System;using SimpleDelegate;Console.WriteLine("***** Simple Delegate Example *****\n");// Делегаты могут также указывать на методы экземпляра.SimpleMath m = new SimpleMath();BinaryOp b = new BinaryOp(m.Add);// Вывести сведения об объекте.DisplayDelegateInfo(b);Console.WriteLine("10 + 10 is {0}", b(10, 10));Console.ReadLine();В данном случае вывод будет выглядеть следующим образом:
***** Simple Delegate Example *****Method Name: Int32 Add(Int32, Int32)Type Name: SimpleDelegate.SimpleMath10 + 10 is 20Отправка уведомлений о состоянии объекта с использованием делегатов
Очевидно, что предыдущий пример
был чисто иллюстративным по своей природе, т.к. нет особых причин создавать делегат просто для того, чтобы сложить два числа. Рассмотрим более реалистичный пример, в котором делегаты применяются для определения классаSimpleDelegate, обладающего способностью информировать внешние сущности о текущем состоянии двигателя. В таком случае нужно выполнить перечисленные ниже действия.Car1. Определить новый тип делегата, который будет использоваться для отправки уведомлений вызывающему коду.
2. Объявить переменную-член этого типа делегата в классе
.Car3. Создать в классе
вспомогательную функцию, которая позволяет вызывающему коду указывать метод для обратного вызова.Car4. Реализовать метод
для обращения к списку вызовов делегата в подходящих обстоятельствах.Accelerate()Для начала создайте новый проект консольного приложения по имени
. Определите в нем новый классCarDelegate, начальный код которого показан ниже:Carusing 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{...<b> // 1. Определить тип делегата.</b>public delegate void CarEngineHandler(string msgForCaller);<b> // 2. Определить переменную-член этого типа делегата.</b>private CarEngineHandler _listOfHandlers;<b> // 3. Добавить регистрационную функцию для вызывающего кода.</b>public void RegisterWithCarEngine(CarEngineHandler methodToCall){_listOfHandlers = methodToCall;}}В приведенном примере обратите внимание на то, что типы делегатов определяются прямо внутри области действия класса
; безусловно, это необязательно, но помогает закрепить идею о том, что делегат естественным образом работает с таким отдельным классом. Тип делегатаCarможет указывать на любой метод, который принимает значениеCarEngineHandlerкак параметр и имеетstringв качестве возвращаемого типа.voidКроме того, была объявлена закрытая переменная-член делегата (
) и вспомогательная функция (_listOfHandlers), которая позволяет вызывающему коду добавлять метод в список вызовов делегата.RegisterWithCarEngine()На заметку! Строго говоря, переменную-член типа делегата можно было бы определить как
, избежав тем самым необходимости в создании дополнительных методов регистрации. Тем не менее, за счет определения этой переменной-члена типа делегата какpublicусиливается инкапсуляция и обеспечивается решение, более безопасное в отношении типов. Позже в главе при рассмотрении ключевого слова event языка C# мы еще вернемся к анализу рисков объявления переменных-членов с типами делегатов как public.privateТеперь необходимо создать метод
. Вспомните, что цель в том, чтобы позволить объектуAccelerate()отправлять связанные с двигателем сообщения любому подписавшемуся прослушивателю. Вот необходимое обновление:Car<b>// 4. Реализовать метод Accelerate() для обращения к списку</b><b>// вызовов делегата в подходящих обстоятельствах.</b>public void Accelerate(int delta)