Mr.ElectroNick
personal website
ГЛАВНАЯ | НОВОСТИ | СТАТЬИ | ОБЗОРЫ | ССЫЛКИ | ДРУЗЬЯ | ФОТКИ | ОБО МНЕ
Главная / Статьи / События и делегаты в ...
 :: Следы в сети
127461658

mr.electronick

View My profile on LinkedIn

 :: Погода
 :: Банеры
LostFilm.TV. Лучшие сериалы на одном канале.

www.nebo.org.ua

События и делегаты в языке C#

Введение

Когда я занялся изучением событий и делегатов я прочитал огромное количество статей документации для того чтобы окончательно разобраться в этом вопросе. Теперь я хочу предложить вам прочитать эту статью и узнать всё самое главное относительно событий и делегатов.

Что такое ДЕЛЕГАТЫ?

Понятия делегатов и событий всецело связаны друг с другом. Делегаты это указатели на функции. Delegate это класс. Когда высоздаете экземпляр этого класса, необходимо передать имя функции параметром конструктора класса делегата. На переданную функцию и будет ссылаться делегат.

Каждый делегат имеет сигнатуру. Делегат объявляется таким образом:

Delegate int SomeDelegate(string s, bool b);

Когда я говорю что делегат имеет сигнатуру, то я имею ввиду что делегат возвращает int и имеет два параметра string и bool.

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

Рассмотрим следующую функцию:

private int SomeFunction(string str, bool bln){...}

Вы можете передать эту функцию в конструктор класса делегата SomeDelegate, потому что сигнатуры делегата и функции соответствуют.

SomeDelegate sd = new SomeDelegate(SomeFunction);

Теперь, sd ссылается на SomeFunction, или другими словами, SomeFunction зарегестрирована в sd. Если вы вызываете sd, SomeFunction будет также вызвана.

sd("somestring", true);

Теперь, когда мы разобрались с делегатами, давайте разберемся с событиями...

Что такое СОБЫТИЯ?

Хотите узнать что в этот момент происходит? Давайте по порядку:

У нас есть класс Counter. Этот класс содержит метод CountTo(int countTo, int reachableNum) который начинает отсчет от 0 до countTo, и запускает событие NumberReached когда значение счета достигает reachableNum.

Наш класс содержит событие: NumberReached. События - это переменные с типом делегата. Я имею ввиду что если вы хотите объявить событие, то вы просто объявляете событие с тем же типом что и делегат и ставите ключевое слово event перед объявлением. Например как сделано сдесь:

public event NumberReachedEventHandler NumberReached;

В вышеописаном объявлении, NumberReachedEventHandler это делегат. Может быть было бы лучше назвать делегат как: NumberReachedDelegate, но обратите внимание что Microsoft не называет системные делегаты например так: MouseDelegate или PaintDelegate, наоборот, спецификация по которой описываются делегаты такова: MouseEventHandler и PaintEventHandler.

Вы видите, перед тем как мы объявляем событие, мы должны определить делегат (обработчик события - event handler). Это должно выглядеть примерно так:

public delegate void NumberReachedEventHandler(
object sender, NumberReachedEventArgs e);

Как вы видите, имя делегата: NumberReachedEventHandler, и его сигнатура содержит возвращаемый тип void и 2 параметра object и NumberReachedEventArgs. Если вы где-либо собираетесь объявить этот делегат, то функция переданная в конструктор делегата должна иметь ту же сигнатуру (это уже упоминалось выше).

Использовали ли вы когда-либо PaintEventArgs или MouseEventArgs в вашем коде для определения положения мыши, где она перемещается, или свойство Graphics объекта вызвавшего событие Paint? В действительности, мы определяем набор данных в классе наслодованом от базового класса EventArgs. Например, нам нужно передать конечное число счетчика, вот как выглядит определение класса:

public class NumberReachedEventArgs : EventArgs
{
    private int _reached;
    public NumberReachedEventArgs(int num)
    {
        this._reached = num;
    }
    public int ReachedNumber
    {
        get
        {
            return _reached;
        }
    }
}

Если нет необходимости передавать в аргументах события какую либо информацию то можно использовать просто класс EventArgs, с минимальным набором параметров.

Теперь всё готово чтобы разглядеть внутренности класса Counter:

namespace Events
{
    public delegate void NumberReachedEventHandler(object sender, 
        NumberReachedEventArgs e);
    /// 
    /// Summary description for Counter.
    /// 
    public class Counter
    {
        public event NumberReachedEventHandler NumberReached;
        
        public Counter()
        {
            //
            // TODO: Add constructor logic here
            //
        }
        public void CountTo(int countTo, int reachableNum)
        {
            if(countTo < reachableNum)
                throw new ArgumentException(
                    "reachableNum should be less than countTo");
            for(int ctr=0;ctr<=countTo;ctr++)
            {
                if(ctr == reachableNum)
                {
                    NumberReachedEventArgs e = new NumberReachedEventArgs(
                        reachableNum);
                    OnNumberReached(e);
                    return;//don't count any more
                }
            }
        }
        protected virtual void OnNumberReached(NumberReachedEventArgs e)
        {
            if(NumberReached != null)
            {
                NumberReached(this, e);//Raise the event
            }
        }
    }

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

OK. Теперь настало время практически использовать класс Counter:

В нашем приложении, используем 2 textboxes txtCountTo и txtReachable:

sample application

И вот обработчик события для события нажатия (click) btnRun:

private void cmdRun_Click(object sender, System.EventArgs e)
{
    if(txtCountTo.Text == "" || txtReachable.Text=="")
        return;
    oCounter = new Counter();
    oCounter.NumberReached += new NumberReachedEventHandler(
        oCounter_NumberReached);
    oCounter.CountTo(Convert.ToInt32(txtCountTo.Text), 
        Convert.ToInt32(txtReachable.Text));
}
private void oCounter_NumberReached(object sender, NumberReachedEventArgs e)
{
    MessageBox.Show("Reached: " + e.ReachedNumber.ToString());
}

Вот синтаксис для инициации обработчика для определенного события:

oCounter.NumberReached += new NumberReachedEventHandler(
oCounter_NumberReached);

Теперь, вы разрбалить в происходящем! Вы инициируете делегат NumberReachedEventHandler.

Также обратите внимание на то что мы используем += вместо =.

Это потому что делегаты - специализированные объекты которые могут содержать ссылки на одну и более функций. Например, если есть ещё онда функция oCounter_NumberReached2 с той же сигнатурой что и oCounter_NumberReached, на обе функции можно ссылаться таким вот образом:

oCounter.NumberReached += new NumberReachedEventHandler(
oCounter_NumberReached);
oCounter.NumberReached += new NumberReachedEventHandler(
oCounter_NumberReached2);

Теперь, после срабатывания события, обе функции будут выполнены одна за одной.

Если где-либо в вашем коде, вы решите что oCounter_NumberReached2 не должна больше вызываться по срабатыванию события NumberReached, вы можете сделать вот так:

oCounter.NumberReached -= new NumberReachedEventHandler(
oCounter_NumberReached2);

В завершении

Не забудте поставить следующие строки в конструкторе вашей формы, вместо cmdRun_Click.

public Form1()
{
    //
    // Required for Windows Form Designer support
    //
    InitializeComponent();
    //
    // TODO: Add any constructor code after InitializeComponent call
    //
    oCounter = new Counter();
    oCounter.NumberReached += new NumberReachedEventHandler(
    oCounter_NumberReached);
    oCounter.NumberReached += new NumberReachedEventHandler(
    oCounter_NumberReached2);

Исходные коды для этой статьи вы можете найти выше.

23/08/2004 11:37
ГЛАВНАЯ | НОВОСТИ | СТАТЬИ | ОБЗОРЫ | ССЫЛКИ | ДРУЗЬЯ | ФОТКИ | ОБО МНЕ

Украинская Баннерная Сеть
Сова - поиск в Украине