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

时间:2011-08-11 08:27:26

标签: 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;
        }
    }
}

如何在Main中注册事件处理程序?

4 个答案:

答案 0 :(得分:5)

阅读How to: Publish Events that Conform to .NET Framework Guidelines,了解有关如何创建和使用事件的更多信息。这个例子并不像它应该那样清楚,所以我将在这里完成这个过程。


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

(如果您希望能够将旧值和新值等信息作为参数包含在内,那么您将使用通用版本以及从EventArgs派生的适当类。有点高级主题所以我们将跳过它。)


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

public event EventHandler NameChanged;

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

有了这一切,只需要调用事件处理程序即可。它只是一个代表,所以只需要它。但是不要忘记先检查它是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());
    }
}

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

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对象,因此我们可以获取已更改的对象,并在需要时检查属性。你可以随意命名。我个人采用的模式是:InstanceName_EventName。所以在这种情况下,我将其命名为: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";
    }
}

答案 1 :(得分:2)

我更改并评论了您的代码,但我不确定您打算做什么:

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;
        }
    }
}

答案 2 :(得分:0)

在您的人员中调用方法并开始在该方法中引发事件。当Person类引发事件时,由于main已订阅此事件。你接到一个回电话进入Main。

您刚刚订阅了此活动。活动体在哪里??

如果您重新获得winforms事件。你订阅了让按钮点击事件,如btn_Click(object,eventArgs)..然后你给这个事件方法的正文吧?同样的方式。

答案 3 :(得分:0)

我真的不明白你的意图,但是:

  1. 您尚未在Program类中订阅将处理名称更改事件的函数。

  2. 您尚未在Person类中触发该事件。你应该写:

    protected void NameChange(EventArgs arg) {     Console.WriteLine(“名称更改...”);     NameChangeEventHandler(); }