000140. Делегаты и события в c#
Last updated
Last updated
Делегаты и события в c#||wmysterio|wmysterio|wmysterio@yandex.ru|||Всем хай! Прошло очень много времени прежде, чем я понял как работают события и как их вызывать. Пришло время передать мой опыт вам! Начнём с ознакомлением нового слова - delegate. Делегат - это объект, который может ссылаться на метод. Его также можно назвать "прототипом метода". Объявляется он как обычный метод, только с приставкой изучаемого нами слова:
СПЕЦИФИКАТОР delegate ТИП ИМЯ ( СПИСОК_ПАРАМЕТРОВ ); |
Так, как делегат - это прототип метода, то он содержит: - тип возвращаемого значения. Обычно его ставят на void, делая из метода процедуру. - имя процедуры или метода - список параметров, которые передаются в метод. Если их нет, то оставляют пустые скобки. Делегат можно объявлять как в пространстве имён, так и внутри класса. В качестве примера создадим делегат внутри класса Car, с которым мы игрались в предыдущих уроках:
public delegate void CarEvent(); |
Я назвал более понятным языком, что бы вам было легче понять. Наш делегат называется "Событие автомобиля". Мы не указываем какое именно, а только общее для всех. В нём не будет параметров. Событие - это реакция на какое-то действие с объектом. Чтобы объявить событие, достаточно написать следующий код:
public event CarEvent Wrecked; |
Мы указали видимость этого события, казали ключевое слово event и прототип метода(наш делегат) и имя события. Наш класс теперь такой:
public class Car { public delegate void CarEvent(); public event CarEvent Wrecked; public Car() { Name = "Автомобиль"; } public string Name { get; set; } public int CurrentSpeed { get; set; } } |
Теперь объект класса получил событие(события обозначаются значком молнии): Но это только видимость того, что у объекта есть событие. Нам нужно указать когда именно будет срабатывать это событие. Раз я назвал событие Wrecked, то есть повреждён, то есть смысл создать свойство Health, которое на которое будет реагировать событие:
private int _health; public int Health { get { return _health; } set { _health = value; if ( 1 > _health && Wrecked != null ) Wrecked(); } } |
Думаю, вы обратили внимание на блок set, а именно на проверку. Расскажу для чего она нужно. Чтобы событие отвечало действительности, нам нужно проверить, что бы жизнь транспорта был меньше нуля а также проверить на то, что событие существует и не вызвано. Теперь изюминка: откуда взялся метод Wrecked()? Я бы назвал его гибридом, так как имя метода формируется из события, а параметры - из делегата, причём параметр делегата называют аргументом, а все аргументы - сигнатурой. Вызывая этот метод мы сообщаем о том, что событие произошло. Теперь наш класс имеет такой вид:
public class Car { public delegate void CarEvent(); public event CarEvent Wrecked; public Car() { Name = "Автомобиль"; Health = 100; } public string Name { get; set; } public int CurrentSpeed { get; set; } private int _health; public int Health { get { return _health; } set { _health = value; if ( 1 > _health && Wrecked != null ) Wrecked(); } } } |
Теперь если указать значение жизни транспорта ниже 1 произойдёт событие Wrecked. Наша задача выполнена - мы написали своё событие в c# без использования стандартных конструкций. Мы можем подписаться и отписываться на события. Для этого служат следующие выражения:
+= | Оформить подписку на событие |
-= | Отписаться от события |
Вот пример подписки на события:
Car MyCar = new Gallardo(); MyCar.Wrecked += new Car.CarEvent( MyCar_Wrecked ); |
Для каждой подписки указывается делегат и, если он внутри класса, класс. В скобках указывается имя функции, которая будет обрабатывать события. В нашем случаи обработчик будет иметь следующий вид:
static void MyCar_Wrecked() { Console.WriteLine("Транспорт уничтожен!"); } |
for ( int i = 50; i > -5; i-- ) { Console.WriteLine("Жизнь транспорта: {0}", i); MyCar.Health = i; } |
MyCar.Wrecked -= new Car.CarEvent( MyCar_Wrecked ); |
Полностью идентичен записи подписки, только со знаком -. Когда использовать это выражение? Давайте напишем код так, чтобы обработчик сообщил только 1 раз, что транспорт уничтожен. так как поля и методы у "главного класса" статические, то объявим переменную DisableEvent типа bool, которая будет сообщать когда отписаться от события:
class Program { static bool DisableEvent = false; static void Main() { Car MyCar = new Gallardo(); MyCar.Wrecked += new Car.CarEvent( MyCar_Wrecked ); for ( int i = 50; i > -5; i-- ) { Console.WriteLine("Жизнь транспорта: {0}", i); MyCar.Health = i; if ( DisableEvent ) MyCar.Wrecked -= new Car.CarEvent( MyCar_Wrecked ); } Console.ReadKey(); } public static void MyCar_Wrecked() { Console.WriteLine("Транспорт уничтожен!"); DisableEvent = true; } } |
object sender, EventArgs e |
Объясню что это такое и откуда оно берётся: object sender - это объект класса который передал событие. EventArgs e - аргументы, которые были указаны в делегате; указывается класс конструктор которого является делегатом. Возвращает публичные поля этого класса. Принято аргументы называть буквой e, (от event) Давайте модифицируем делегат с этого примера:
public delegate void CarEvent( object sender, int e); |
Также нам нужно отредактировать вызов события под эту сигнатуру:
if ( 1 > _health && Wrecked != null ) Wrecked(this, _health); |
this - указываем объект, который передаёт событие, в нашим случаи передастся Car. В качестве аргумента e передаём поле класса - _health. Теперь переработаем обработчик под сигнатуру:
public static void MyCar_Wrecked( object sender, int e ) { Console.WriteLine("Транспорт уничтожен!"); DisableEvent = true; } |
Не знаю как в других версиях Visual Studio(я использую 2010-ю), но при написании += компилятор автоматически предлагает написать обработчик и делегат: Нажимаем 2 раза TAB, и делегат с обработчиком автоматически вставятся к поле кода. Достаточно удобная возможность, не так ли? Так, подписка оформлена, обработчик написан. Осталось только проверить работает ли наше событие. для этого воспользуемся циклом for чтобы имитировать состояние жизни транспорта:
Результат: Вывод: всякий раз, когда мы задаём жизнь транспорта меньше единицы срабатывает событие и сообщает в консоль, что транспорт уничтожен. Помню, Vital в своих видео-уроках по c# спрашивал как сделать так, что бы событие срабатывало только один раз. Отвечаю на этот вопрос. Чтобы событие больше не срабатывало, нужно использовать выражение -= вместо +=. В этом примере это будет так:
Результат: Во многих случаях при использовании уже готовых событий у обработчика бывают какие-то "непонятные выражения" типа
Теперь поле _health будет передано через аргумент e, хоть и само поле не будет доступно через класс.
Исходный код:Кодusing System;
namespace Consol {
class Program {
static bool DisableEvent = false;
static void Main() {
Car MyCar = new Gallardo();
MyCar.Wrecked += new Car.CarEvent( MyCar_Wrecked );
for ( int i = 50; i > -5; i-- ) {
Console.WriteLine("Жизнь транспорта: {0}", i);
MyCar.Health = i;
if ( DisableEvent )
MyCar.Wrecked -= new Car.CarEvent( MyCar_Wrecked );
}
Console.ReadKey();
}
public static void MyCar_Wrecked( object sender, int e ) {
Console.WriteLine("Транспорт уничтожен! {0}", (e + 55).ToString());
DisableEvent = true;
}
}
public class Car {
public delegate void CarEvent( object sender, int e);
public event CarEvent Wrecked;
public Car() {
Name = "Автомобиль";
Health = 100;
}
public string Name { get; set; }
public int CurrentSpeed { get; set; }
private int _health;
public int Health {
get {
return _health;
}
set {
_health = value;
if ( 1 > _health && Wrecked != null )
Wrecked(this, _health);
}
}
}
public class Lanborghini : Car {
public int DoorCount = 4;
public string EngineType = "Automatic";
public Lanborghini() {
Name = "Ламборгини";
}
}
public class Porshe : Car {
public int DoorCount = 2;
public string EngineType = "Automatic";
public int WheelsSize = 5;
public Porshe() {
Name = "Порше";
}
}
public class Gallardo : Lanborghini {
public int EnginePower = 1500;
public int TireSize = 4;
public Gallardo() {
Name = "Галлардо";
}
}
}
"Вот такие у нас пироги" С вами был wmysterio.|1401|1|0|53890969png
172171\|36227451
png563
54400
38|02431911png
168159\|02856878
png181
134||delegaty_i_sobytija_v_c|1392798603