Регистрация обработчика для события в текущем классе с пользовательским делегатом - PullRequest
3 голосов
/ 11 августа 2011

Я определил событие в своем классе, и я хочу сделать один из методов класса обработчиком события.

Это то, что у меня пока есть:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DelegatesAndEvents
{
    class Program
    {
        static void Main(string[] args)
        {
            Person p = new Person();
            p.NameChangeEventHandler += new Person.NameChangeEventHandlerDel;

            p.Name = "Paul";
        }
    }

    class Person
    {
        #region Events
        public delegate void NameChangeEventHandlerDel(object sender, EventArgs args);
        public event EventHandler NameChangeEventHandler;
        protected void NameChange(EventArgs arg)
        {
            Console.WriteLine("Name change...");
        }
        #endregion

        #region Properties
        private string name;
        public string Name
        {
            get { return name; }
            set 
            {
                NameChange(null);
                name = value;
            }
        }
        #endregion

        public Person(string name = "John")
        {
            this.name = name;
        }
    }
}

Как я могу зарегистрировать обработчик событий без необходимости делать это в Main?

Ответы [ 4 ]

5 голосов
/ 11 августа 2011

Прочитайте Как: публиковать события, соответствующие рекомендациям .NET Framework для получения дополнительной информации о том, как создавать и использовать события.Приведенный здесь пример не так ясен, как следовало бы, поэтому я рассмотрю этот процесс здесь.


Первая часть, необходимая для определения события, - это используемый делегат обработчика события.Это подпись метода, которая требуется всем, кто хочет получать уведомления об этом событии.Как правило, вам не нужно создавать новый делегат самостоятельно, вы должны использовать существующий EventHandler (или универсальный EventHandler<TEventArgs>) делегат.Вы бы использовали неуниверсальную версию, если вам не нужно включать какие-либо другие аргументы о событии.В противном случае вы бы использовали общую версию.В вашем случае нет никаких других аргументов, необходимых для события, поэтому вы должны использовать неуниверсальный EventHandler.

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


Следующая часть должна определить событие.Это так же просто, как если бы вы определяли свойство для своего класса.Разница здесь в том, что вы используете ключевое слово event, чтобы указать, что вы определяете событие, за которым следует использовать делегат, а также имя события.Соглашение о присвоении имени событию для изменения свойств обычно заключается в шаблоне <i>PropertyName</i>Changed.Поскольку вам нужно событие, которое вызывается при изменении свойства Name, вам следует назвать его: NameChanged.

public event EventHandler NameChanged;

Необязательный (но настоятельно рекомендуемый) шаг - определить методиспользуется для поднятия события.Это поможет вам поднять событие, когда вы хотите.Обычное соглашение об именах похоже на то, как названо событие.На этот раз On<i>EventName</i>.Так что вы бы назвали это OnNameChanged здесь.Обычно он определяется как защищенный виртуальный метод, чтобы производные классы могли легко его переопределить.Аргументы функции должны быть аргументами, необходимыми для события.Поскольку здесь нет аргументов, его можно оставить без аргументов в сигнатуре.

Теперь, когда все это на месте, нужно просто вызывать обработчики событий.Это просто делегат, так что просто позвоните.Но не забудьте проверить, если сначала null, это будет означать, что обработчики событий не были зарегистрированы.Аргументы обработчика должны быть this (объект, который вызывает событие) и какими бы ни были аргументы.В этом случае аргументов нет, но вы должны вернуть экземпляр «пустого» * ​​1035 *.

protected virtual void OnNameChanged()
{
    EventHandler nameChanged = NameChanged; // always a good idea to store in a local variable

    if (nameChanged != null)
    {
        nameChanged(this, new EventArgs());
    }
}

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

private string name;
public string Name
{
    get { return name; }
    set 
    {
        if (!String.Equals(value, name)) // if the value gets changed...
        {
            name = value;
            OnNameChanged(); // raise the event!!!
        }
    }
}

Теперь, когда все готово к событию, вы хотите иметь возможность зарегистрировать некоторые обработчики для этого события.Чтобы сделать это, сначала нам понадобится экземпляр Person, который мы хотим дождаться Name изменений, и нам нужен метод обработки события с правильной сигнатурой для события.Событие определено для использования делегата EventHandler, поэтому нам нужен метод с подписью: void NameChanged(object sender, EventArgs e).Помните, что аргумент sender - это объект, вызвавший событие.В данном случае это объект Person, поэтому мы можем получить измененный объект и проверить свойства, если захотим.Вы можете назвать это как хотите.Шаблон, который я использую лично: <i>InstanceName</i>_<i>EventName</i>.Поэтому в этом случае я бы назвал его: person_NameChanged.

static void person_NameChanged(object sender, EventArgs e)
{
    Person person = (Person)sender; // cast back to a Person so we can see what's changed

    Console.WriteLine("The name changed!");
    Console.WriteLine("It is now: " + person.Name); // let's print the name
}

Как только это будет определено, добавьте обработчик к событию, и мы будем уведомлены, если что-то изменится.

person.NameChanged += person_NameChanged;

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

public Person(string name = "John")
{
    this.name = name;
    this.NameChanged += builtin_NameChanged;
}

private static void builtin_NameChanged(object sender, EventArgs e)
{
    Person person = (Person)sender; // cast back to a Person so we can see what's changed

    Console.WriteLine("The name changed!");
    Console.WriteLine("It is now: " + person.Name); // let's print the name
}

Обратите внимание, что в этом конкретном примере обработчик является статическим, поэтому он непривязан к одному экземпляру.Это будет работать для любого Person в целом.Просто имейте в виду, что, поскольку он статический, вы не сможете использовать this в методе, поэтому необходимо использовать приведение к отправителю.Независимо от того, статично это или нет, зависит от вас, в любом случае все должно быть в порядке.


Итак, все это вместе, вот что вы могли бы сделать:

class Person
{
    public Person(string name = "John")
    {
        this.name = name;
        this.NameChanged += builtin_NameChanged;
    }

    public string Name
    {
        get { return name; }
        set 
        {
            if (!String.Equals(value, name))
            {
                name = value;
                OnNameChanged();
            }
        }
    }

    public event EventHandler NameChanged;

    protected virtual void OnNameChanged()
    {
        EventHandler nameChanged = NameChanged;

        if (nameChanged != null)
        {
            nameChanged(this, new EventArgs());
        }
    }

    private static void builtin_NameChanged(object sender, EventArgs e)
    {
        Person person = (Person)sender;

        Console.WriteLine("The name changed!");
        Console.WriteLine("It is now: " + person.Name);
    }

    private string name;
}

class Program
{
    static void Main(string[] args)
    {
        Person person = new Person();
        person.Name = "Paul";
    }
}
2 голосов
/ 11 августа 2011

Я изменил и прокомментировал ваш код, но я не совсем уверен, что вы собираетесь делать:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DelegatesAndEvents
{
    class Program
    {
        static void Main(string[] args)
        {
            Person p = new Person();
            //here you have an event of type EventHandler and want to subscribe with a delegate of type 
            //NameChangeEventHandlerDel. This can't work. Furthermore you have to create the delegate passing 
            //a method.
            //p.NameChangeEventHandler += new Person.NameChangeEventHandlerDel;
            // this could be better (even if I'm not sure about the necessity to use an event, 
            //but it probably depends on what you really are trying to do):
            p.NameChangeEventHandler += new EventHandler(p.NameChange);

            p.Name = "Paul";
        }
    }

    class Person
    {
        #region Events
        // why do you define the delegate NameChangeEventHandlerDel and then declare the event of type EventHandler?
        public delegate void NameChangeEventHandlerDel(object sender, EventArgs args);
        public event EventHandler NameChangeEventHandler;

        protected void NameChange(EventArgs arg)
        {
            Console.WriteLine("Name change...");
        }
        #endregion

        #region Properties
        private string name;
        public string Name
        {
            get { return name; }
            set 
            {
                // here you should not call your method directly, but trigger the event (if this is what you want)
                //i.e not: NameChange(null); but something like:
                if (NameChangeEventHandler != null)
                    NameChangeEventHandler(this, null);
                name = value;
            }
        }
        #endregion

        public Person(string name = "John")
        {
            this.name = name;
        }
    }
}
0 голосов
/ 11 августа 2011

Я не совсем понимаю, каковы ваши намерения, но:

  1. вы не подписали функцию в классе Program, которая будет обрабатывать событие изменения имени.

  2. Вы не запустили событие в классе Person. Вы должны написать:

    защищенный void NameChange (EventArgs arg) { Console.WriteLine («Изменение имени ...»); NameChangeEventHandler (); }

0 голосов
/ 11 августа 2011

Вызовите метод в своей персоне и начните вызывать события в этом методе.Каждый раз, когда событие вызывается классом Person, так как основной подписался на это событие.Вам перезвонят в Main.

Вы только что подписались на событие.Где находится тело события ??

Если вы напоминаете события winforms.Вы подписываетесь, скажем, на событие нажатия кнопки, например btn_Click (object, eventArgs). Тогда вы дадите тело для этого метода события, верно?Точно так же и здесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...