Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

События C# по-человечески

Anna | 17.06.2014 | нет комментариев
Немыслимо, легко взять и вникнуть в данный большой толк, исследуя События (event) в просторах базового и, на 1-й взор, безмерного C#.

Когда я постигал События (не в рамках .NET!), потратил много сил, Дабы, наконец-то, разобраться, как они устроены и обязаны конструироваться. Следственно, я решил опубликовать свою методологию понимания конструкции пользовательского события, коим представляется ключевое слово event в С#.
Не буду цитировать и без того замученную MSDN, а постараюсь объяснить ясно и доступно.

Событие, это не что иное, как обстановка, при появлении которой, произойдет действие либо несколько действий. Говоря языком программного моделирования, Событие — это именованный делегат, при вызове которого, будут запущены все подписавшиеся на момент вызова события способы заданной сигнатуры. Эта трактовка хоть и раскрывает всю суть конструкции события, но не только сбивает с толку начинающих «шарп-прогеров», но и не дает вероятность осмысленно представить в программистской голове каждый толк.

Выходит, Событие, это обстановка, при появлении которой, произойдут некоторые действия. Само событие имеет определенную конструкцию.

Представим, что стоит такая задача: определено три класса. 1-й класс будет считать до 100, применяя цикл. Два других класса будут ожидать, когда в первом классе счетчик досчитает, скажем, до 71, и позже этого всякий выведет в консоль фразу «Пора делать, чай теснее 71!». Проще говоря, при выявлении значения 71, вызовутся по способу, соответственно для всякого класса. Разложим все по полкам.

1. Моделирование обстановки.

Подготовим эти три простейших класса, оставив точку входа в программу main нетронутой.
Класс ClassCounter и его способ Count() в котором будет производится счет. (В коде я спускаю пространства имен namespace, потому как это ясно, как день).

    class ClassCounter  //Это класс - в котором производится счет.
    {
        public void Count()
        {
            //Здесь будет производиться счет
        }
    }

Два других класса (имена им Handler_I и Handler_II), которые обязаны реагировать на появление события способами public void Message(). У всякого по способу, как и договаривались.

    class Handler_I //Это класс, реагирующий на событие (счет равен 71) записью строки в консоли.
    {
        public void Message()
        {
            //Не позабудьте using System 
            //для итога в консольном приложении
            Console.WriteLine("Пора делать, чай теснее 71!"); 
        }                                                        
    }
    class Handler_II
    {
        public void Message()
        {
            Console.WriteLine("Верно, теснее 71!");
        }    
    }

Напомню, когда счетчик будет считать до 100 и достигнет 71, обязаны сработать способы Message() для классов Handler_I и Handler_II.
Сейчас возвратимся к классу ClassCounter и сотворим счетчик при помощи цикла for c переменной-счетчикомint i.

    class ClassCounter  //Это класс - в котором производится счет.
    {
        public void Count()
        {
            for (int i = 0; i < 100; i  )
            {
            }
        }
    }

1-й этап закончен. У нас есть класс счетчик и два класса, которые будут выводить сообщения. Данные задачи: как только i=71, обязаны сработать способы Message() для 2-х классов Handler_I и Handler_II.

2. Оформление события.

Абстрагируемся от программирования. Событие, которое мы хотим сделать, будет представлять фразу “… счетчик считает. И как только он будет равен 71, обязаны выполниться действия”. Значит, нам нужно условие «как только он будет равен 71». Предположим его при помощи условного оператора if.

    class ClassCounter  //Это класс - в котором производится счет.
    {
        public void Count()
        {
            for (int i = 0; i < 100; i  )
            {
                if (i == 71)
                {
                }
            }
        }
    }

Конструируем событие event. Определяем по способам, которые обязаны сработать при i=71 их сигнатуру(либо прототип).
Сигнатура способа — это так называемая спецификация (либо примитивными словами «образец») какого-л. способа либо способов. Представляет собой сочетание наименования типа, тот, что способ возвращает, плюс наименование типов входящих параметров (по порядку! порядок дюже значим.)
Скажем, способ int NewMethod(int x, char y) будет иметь сигнатуру int (int, char), а способ void NewMethod()— void (void).
Как толкует MSDN, события (event) основаны на делегатах (delegate), а делегат, говоря дюже простым языком— «переменная, хранящая ссылку на способ». Как Вы теснее осознали, т.к. наше событие будет ссылаться на два способа void Message(), мы обязаны определить сигнатуру этих способов, и составить на основе этой сигнатуры делегат. Сигнатура выглядит так: void (void).

Определяем делегат (назовем его MethodContainer):

    class ClassCounter  //Это класс - в котором производится счет.
    {
        //Синтаксис по сигнатуре способа, на тот, что мы создаем делегат: 
        //delegate <выходной тип> ИмяДелегата(<тип входных параметров>);
        //Мы создаем на void Message(). Он должен запуститься, когда условие выполнится.

        public delegate void MethodContainer(); 

        public void Count()
        {
            for (int i = 0; i < 100; i  )
            {
                if (i == 71)
                {
                }
            }
        }
    }

Дальше, мы создаем событие при помощи ключевого слова event и объединяем его с этим делегатом (MethodContainer), а, следственно, c способами, имеющими сигнатуру void (void). Событие должно быть public, т.к. его обязаны применять различные классы, которым необходимо как-то отреагировать (классы Handler_I и Handler_II).
Событие имеет синтаксис: public event <НазваниеДелегата> <НазваниеСобытия>;
Наименование делегата — это имя делегата, на тот, что «ссылаются» наши способы.

 class ClassCounter  //Это класс - в котором производится счет.
    {
        public delegate void MethodContainer();

        //Событие OnCount c типом делегата MethodContainer.
        public event MethodContainer onCount;

        public void Count()
        {
            for (int i = 0; i < 100; i  )
            {
                if (i == 71)
                {
                }
            }
        }
    }

Сейчас запустим наше событие onCount, в условии когда i=71:

if (i == 71)
{
     onCount();
}

Все. Событие сделано. Способы, которые вызовет это событие, определены по сигнатурам и на основе их сделан делегат. Событие, в свою очередь, сделано на основе делегата. Пора показать событию onCount, какие же все-таки способы обязаны сработать (мы чай указали только их сигнатуру).

3. Подписка.

Возвратимся в точку входа программы main и сделаем экземпляр класса ClassCounter. А также сотворим по экземпляру классов, которые обязаны запуститься. (Они обязаны быть public).

    class Program
    {
        static void Main(string[] args)
        {
            ClassCounter Counter = new ClassCounter();
            Handler_I Handler1 = new Handler_I();
            Handler_II Handler2 = new Handler_II();
        }
    }

Сейчас укажем событию onCount, способы, которые обязаны запуститься.
Происходит это дальнейшим образом: <КлассИлиОбъект>.<ИмяСобытия> = <КлассЧейМетодДолженЗапуститься>.<МетодПодходящийПоСигнатуре>.
Никаких скобочек позже способа! Мы же не вызываем его, а легко указываем его наименование.

    class Program
    {
        staticvoid Main(string[] args)
        {
            ClassCounter Counter = new ClassCounter();
            Handler_I Handler1 = new Handler_I();
            Handler_II Handler2 = new Handler_II();

            //Подписались на событие
            Counter.onCount  = Handler1.Message;
            Counter.onCount  = Handler2.Message;
        }
    }
Проверка.

Сейчас осталось запустить счетчик класса ClassCounter и подождать, пока i станет равным 71. Как только i=71, запустится событие onCount по делегату MethodContainer, тот, что (в свою очередь) запустит способыMessage(), которые были подписаны на событие.

    class Program
    {
        static void Main(string[] args)
        {
            ClassCounter Counter = new ClassCounter();
            Handler_I Handler1 = new Handler_I();
            Handler_II Handler2 = new Handler_II();

            Counter.onCount  = Handler1.Message;
            Counter.onCount  = Handler2.Message;

            //Запустили счетчик
            Counter.Count();
        }
    }

Итог:
Пора делать, чай теснее 71!
Верно, теснее 71!

Завершение.

Постарайтесь осознать толк и порядок создания события.

  • 1. Определите условие происхождения события и способы которые обязаны сработать.
  • 2. Определите сигнатуру этих способов и сделайте делегат на основе этой сигнатуры.
  • 3. Сделайте общедоступное событие на основе этого делегата и вызовите, когда условие сработает.
  • 4. Непременно (где-желательно) подпишитесь на это событие теми способами, которые обязаны сработать и сигнатуры которых подходят к делегату.

Класс, в котором вы создаете событие (генерируете) именуется классом-издателем, а классы, чьи способы подписываются на это событие при помощи ” =” — классами-подписчиками.

Запомните! Если Вы не подписались на событие и его делегат пустой, возникнет оплошность.
Дабы избежать этого, нужно подписаться, либо не вызывать событие вообще, как показано на примере (Т.к. событие — делегат, то его неимение является «нулевой ссылкой» null).

                if (i == 71)
                {
                    if (onCount != null)
                    {
                        onCount();
                    } 
                }

Вы неизменно можете отписаться, применяя оператор “-=“: <КлассИлиОбъект>.<ИмяСобытия> -= <КлассЧейМетодДолженЗапуститься>.<МетодПодходящийПоСигнатуре>.

Преобладание Событий видимо: классу-издателю, генерирующему событие не необходимо знать, сколькоклассов-подписчиков подпишется либо отпишется. Он сотворил событие для определенных способов, ограничив их делегатом по определенной сигнатуре.
События обширно применяются для составления собственных компонентов управления (кнопок, панелей, и т.д.).

У самых маленьких может появиться вопрос: что делать, если способы, которые обязаны сработать имеют входящий параметр (а то и не один!)?
Результат: Все дело в делегате, на котором основывается событие. А вернее сигнатура подходящих для делегата способов. Когда Вы сконструируете делегат, «принимающий» способ с параметром, то (!) при запуске событие запросит данный параметр. Безусловно, параметр может быть чем желательно.

Пару слов о .NET-событиях. Microsoft упростила задачу конструирования делегатов: .NET предлагает готовый делегат EventHandler и т.н. «пакет» входных параметров EventArgs. Хотите событие? Берете готовый EventHandler, определяетесь в параметрах, «запихиваете» их в класс, а класс наследуете от EventArgs. А дальше — как по расписанию)

На самом деле многие разработчики утверждают (и я с ними согласен), что основная задача «недопонимания» событий — их специфическая область использования, а в итоге — немного доступных примеров. Ну и не забывайте о практике.

P.S. Если вы не ни разу не применяли делегаты, отменнее испробуйте потренироваться на делегатах, а после этого попытайтесь осознать эту статью.
Я верю, что внес малое осознавание в эту непростую тему. Фуроров!

Источник: programmingmaster.ru
Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB