Рейтинг - 5.0 (3)

Всем хай! Прошло очень много времени прежде, чем я понял как работают события и как их вызывать.

Пришло время передать мой опыт вам! Начнём с ознакомлением нового слова - 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("Транспорт уничтожен!");
        }
Не знаю как в других версиях Visual Studio(я использую 2010-ю), но при написании += компилятор автоматически предлагает написать обработчик и делегат:

Нажимаем 2 раза TAB, и делегат с обработчиком автоматически вставятся к поле кода. Достаточно удобная возможность, не так ли?

Так, подписка оформлена, обработчик написан. Осталось только проверить работает ли наше событие. для этого воспользуемся циклом for чтобы имитировать состояние жизни транспорта:
            for ( int i = 50; i > -5; i-- ) {
                Console.WriteLine("Жизнь транспорта: {0}", i);
                MyCar.Health = i;
            }
Результат:

Вывод: всякий раз, когда мы задаём жизнь транспорта меньше единицы срабатывает событие и сообщает в консоль, что транспорт уничтожен.

Помню, Vital в своих видео-уроках по c# спрашивал как сделать так, что бы событие срабатывало только один раз. Отвечаю на этот вопрос. Чтобы событие больше не срабатывало, нужно использовать выражение -= вместо +=. В этом примере это будет так:
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;
        }
Теперь поле _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 = "Галлардо";
        }
    }

}

"Вот такие у нас пироги" smile С вами был wmysterio.

Теги: Делегаты и события в c#
Вход на сайт

Поиск
Категории раздела
Мини-чат
Пожалуйста, все вопросы по скриптингу задавать на форуме!
Наш опрос
Как Вы относитесь к ситуации с OpenIV?
Всего ответов: 8
Активность на сайте
Пожертвования
Кошельки WebMoney:
U859420971000
R407741810602
Z331072372430
E314272616890
Друзья сайта
Полезные ресурсы
Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

Сегодня нас посетили:
wmysterio
Реклама