Копаясь в книге Троэлсена по-поводу делегатов заметил такую забавную штуку что делегаты могут использоваться на манер событий и собсвтенно ввесь вопрос у меня в методе регистратора делегата который выглядит следующим образом
public class Car
{
// 1. Определить тип делегата.
public delegate void CarEngineHandler(string msgForCaller);
// 2. Определить переменную-член типа этого делегата.
private CarEngineHandler listOfHandlers;
// 3. Добавить регистрационную функцию для вызывающего кода.
public void RegisterWithCarEngine(CarEngineHandler methodToCall)
{
listOfHandlers = methodToCall;
}
}
Здесь метод RegisterWithCarEngine принимает объект делегата в в качестве параметра и затем дальше определяем метод для вызова делегата
// 4. Реализовать метод Accelerate() для обращения
// к списку вызовов делегата при нужных условиях.
public void Accelerate(int delta)
{
// Если этот автомобиль сломан, отправить сообщение об этом.
if (carIsDead)
{
if (listOfHandlers != null)
listOfHandlers("Sorry, this car is dead...");
}
else
{
CurrentSpeed += delta;
// Автомобиль почти сломан?
if (10 == (MaxSpeed - CurrentSpeed) && listOfHandlers != null)
{
listOfHandlers("Careful buddy! Gonna blow!");
}
if (CurrentSpeed >= MaxSpeed)
carIsDead = true;
else
Console.WriteLine("CurrentSpeed= {0}", CurrentSpeed);
}
}
И после всего этого в main'е вызываем метод для регистрации делегата,но сделано это не совсем для меня привычным образом так вроде как передаётся анонимный объект делегата(путём вызова конструктора и передаче ему имени метода) который затем присваивается тому полю что объявлено в классе для хранения информации о том что за метод будет вызван при наступлении события(здесь как бы делегатами это сделано поэтому не путайте с событиями) и вот собственно код который вызвал у меня вопрос и я хотел бы уточнить верно ли что передаётся анонимный объект делегата
class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** Delegates as event enablers *****\n");
// Сначала создать объект Car.
Car cl = new Car("SlugBug", 100, 10);
// Теперь сообщить ему, какой метод вызывать,
// когда он захочет отправить сообщение.
cl.RegisterWithCarEngine(new Car.CarEngineHandler(OnCarEngineEvent));
// Ускорить (это инициирует события).
Console.WriteLine("***** Speeding up *****");
for (int i = 0; i < 6; i++)
cl.Accelerate(20);
Console.ReadLine();
}
// Это цель для входящих сообщений.
public static void OnCarEngineEvent(string msg)
{
Console.WriteLine("\n***** Message From Car Object *****");
Console.WriteLine("=> {0}", msg);
Consоle.WriteLine("* * *\n");
}
}
Чтобы прямо совсем конкретно то интересует строка с вызовом метода RegisterWithCarEngine и его аргумент которым по факту является вызов метода new что именно здесь произойдёт. Типа создастя объект делегата без имени и затем мы его передадим тому полю что есть в каждом объекте класса Car и таким образом запишем какой метод нужно вызывать(мне было бы понятнее если бы скажем передали переменную делегата,но здесь сделано так и я хочу уточнить верно ли понял)
В современных объектно-ориентированных языках принято правило всё является объектом. Это относится к Smalltalk, к Java и к C#.
Именно поэтому целые числа в C# это не примитивные сущности, а объекты класса System.Int32
. То же самое и с указателями на функции, которые встречаются в C и C++. Для каждого такого указателя компилятор неявно генерирует класс-наследник от System.Delegate
.
Взглянем на строку:
cl.RegisterWithCarEngine(new Car.CarEngineHandler(OnCarEngineEvent));
Здесь создаётся новый объект неявно сгенерированного класса Car.CarEngineHandler
. Обратите внимание, что вы нигде явно этот класс не описывали, вы написали:
public delegate void CarEngineHandler(string msgForCaller);
а компилятор сгенерировал нужный класс и подходящую реализацию нескольких методов, в частности, Invoke
.
Впрочем, в качестве синтаксического сахара вы можете не создавать объект явно и писать короткий код:
cl.RegisterWithCarEngine(OnCarEngineEvent);
Но в этом случае компилятор всё равно сделает то же самое, что вы писали вручную выше.
То есть да, в вашем коде будет создан объект-делегат, который будет содержать один адрес метода и этот объект будет скопирован в поле listOfHandlers
.
Теперь о том, что из себя представляют делегаты. Исторически, когда придумывали C#, предполагали, что делегаты будут использоваться для реализации механизма событий, то есть в них можно будет хранить несколько методов-подписчиков. Виделось это так, что будут одиночные делегаты (single cast delegates), которые хранят только один адрес метода, и множественные делегаты (multi cast delegates), в которых хранятся много адресов методов.
Потом оказалось, что это всё усложняет, и делегаты в конечном счёте сделали универсальными. В .NET нет одиночных делегатов, в каждом делегате всегда может храниться много адресов методов. Тут наверное следует напомнить, что если вы используете метод класса (статический), то хранится только адрес метода, а если метод экземпляра, то хранится и адрес метода, и адрес подписанного объекта.
Теперь о том, как связаны между собой делегаты и события. Приблизительно также, как поля и свойства. К полю класса вы имеете полный доступ: можете его читать, изменять, получать его адрес. Свойства закрывают прямой доступ, и вы из всех возможностей получаете только возможность читать и писать, да и то не обязательно. Это позволяет вам не только скрывать и инкапсулировать реализацию, но ещё и позволяет применять в языке множественное наследование интерфейсов, за счёт того, что в интерфейсе нет полей, а есть только абстрактные виртуальные методы.
Точно также и событие (event) ограничивает доступ к делегату двумя операциями: добавление нового метода и удаление существующего метода, add
и remove
. Вы уже не можете, например, вызвать событие снаружи класса (только изнутри), и не можете очистить его полностью, хотя, если бы это было поле-делегат, вы могли бы вызывать его даже снаружи.
Ну, и как в случае со свойствами, вы можете описывать события в интерфейсах, но не можете описывать в них поля-делегаты.
Наконец, последнее, про операции +=
и -=
. Это тоже синтаксический сахар. Делегаты — неизменяемые объекты (immutable), поэтому вы не можете добавить к делегату ещё один метод (+=
). Когда вы так пишите, компилятор вызывает статический метод Delegate.Combine
, который создаёт новый делегат из старого делегата и вашего метода. Но для события +=
означает, что компилятор вызовет неявно сгенерированный метод add
для этого события.
listOfHandlers += rnethodToCall;
...
cl.RegisterWithCarEngine(OnCarEngineEvent);
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Создать такую иерархию классов A, B и C, чтобы код ниже компилировался и выводил текст "ABC"