使用自定义委托在当前类上注册事件的处理程序

Jas*_*n94 3 c# events delegates

我在我的类上定义了一个事件,我想让该类的一个方法成为事件的处理程序.

这是我到目前为止:

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

如何注册事件处理程序而不必在Main中执行?

Jef*_*ado 5

阅读如何:发布符合.NET Framework准则的事件,以获取有关如何创建和使用事件的更多信息.那里的例子不太清楚,所以我将在这里介绍这个过程.


定义事件所需的第一部分是要使用的事件处理程序委托.这是所有希望收到有关此活动的通知的人所需的方法签名.您通常不必自己创建新的委托,您应该使用现有EventHandler(或通用EventHandler<TEventArgs>)委托.如果您不需要包含有关该事件的任何其他参数,则可以使用非泛型版本.否则你将使用通用版本.在您的情况下,没有任何关于该事件的其他参数,因此您应该使用非泛型EventHandler.

(如果您希望能够将旧值和新值等信息作为参数包含在内,那么您将使用通用版本和派生自的相应类EventArgs.这是一个高级主题,因此我们将跳过它.)


下一部分是定义事件.就像你为你的班级定义一个属性一样简单.这里的区别在于您使用event关键字来指定您要定义的事件后跟要使用的委托和事件的名称.命名事件以更改属性的约定通常在模式中PropertyNameChanged.由于您需要在Name属性更改时触发的事件,因此您应该将其命名为:NameChanged.

public event EventHandler NameChanged;
Run Code Online (Sandbox Code Playgroud)

可选(但强烈推荐)的步骤是定义用于引发事件的方法.这将使您更容易在需要时举起活动.通常的命名约定类似于事件的命名方式.这一次,.所以你在这里命名.它通常被定义为受保护的虚方法,派生类可以轻松地覆盖它.函数的参数应该是事件所需的参数.由于此处没有参数,因此签名中可能没有参数.OnEventNameOnNameChanged

有了这一切,只需要调用事件处理程序.它只是一个代表,所以只需要它.但是不要忘记检查它是否是null第一个,这表示没有注册事件处理程序.处理程序的参数应该是this(引发事件的对象)和参数应该是什么.在这种情况下,没有参数,但你应该返回一个"空"的实例EventArgs.

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

    if (nameChanged != null)
    {
        nameChanged(this, new EventArgs());
    }
}
Run Code Online (Sandbox Code Playgroud)

最后一部分是在你的属性中连接它.如果赋值将更改属性的值,则您希望引发事件.非常简单,只需检查旧值是否与新值不同.如果是,请更改它并引发事件.否则,别做什么.

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!!!
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们已经完成了事件的设置,您希望能够为此事件注册一些处理程序.为了能够做到这一点,首先我们需要Person我们想要等待Name更改的实例,并且我们需要一个具有正确的事件签名的事件处理方法.该事件被定义为使用EventHandler委托,因此我们需要一个带签名的方法:void NameChanged(object sender, EventArgs e).请记住,sender参数是引发事件的对象.在这种情况下,它是一个Person对象,因此我们可以获取已更改的对象,并在需要时检查属性.你可以随意命名.我亲自去的模式是:.所以在这种情况下,我给它命名:.InstanceName_EventNameperson_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
}
Run Code Online (Sandbox Code Playgroud)

定义完成后,将处理程序添加到事件中,如果有任何更改,我们会收到通知.

person.NameChanged += person_NameChanged;
Run Code Online (Sandbox Code Playgroud)

如果您希望处理程序完全在类中,您可以在类中注册该事件.无需外接电线.你可以从构造函数中完成它.我不建议将您的代码添加到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
}
Run Code Online (Sandbox Code Playgroud)

请注意,在此特定示例中,处理程序是静态的,因此它不依赖于单个实例.它适用于任何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";
    }
}
Run Code Online (Sandbox Code Playgroud)