了解C#中的事件和事件处理程序

我理解事件的目的,特别是在创建用户界面的情况下。 我认为这是创建活动的原型:

public void EventName(object sender, EventArgs e);

事件处理程序做什么,它们为什么需要,以及如何创建一个?


要理解事件处理程序,您需要了解代表。 在C#中,您可以将委托视为指向方法的指针(或引用)。 这很有用,因为指针可以作为值传递。

代表的中心概念是其签名或形状。 这是(1)返回类型和(2)输入参数。 例如,如果我们创建一个委托void MyDelegate(object sender, EventArgs e) ,它只能指向返回void方法,并获取一个objectEventArgs 。 有点像方孔和方形挂钉。 所以我们说这些方法与代表具有相同的签名或形状。

因此,知道如何创建对方法的引用,让我们考虑一下事件的目的:当系统中某处发生某些事情时 - 或者“处理事件”时,我们想要引发一些代码。 为此,我们为想要执行的代码创建特定的方法。 事件和要执行的方法之间的胶水是代表。 当事件发生时,事件必须在内部存储指向方法的指针“列表”。*当然,为了能够调用方法,我们需要知道传递给它的参数。 我们使用委托作为事件与将要调用的所有特定方法之间的“契约”。

所以默认的EventHandler (和许多像它一样)表示方法的特定形状(同样,void / object-EventArgs)。 当你声明一个事件时,通过指定一个委托来说明事件将调用哪种形式的方法(EventHandler):

//This delegate can be used to point to methods
//which return void and take a string.
public delegate void MyEventHandler(string foo);

//This event can cause any method which conforms
//to MyEventHandler to be called.
public event MyEventHandler SomethingHappened;

//Here is some code I want to be executed
//when SomethingHappened fires.
void HandleSomethingHappened(string foo)
{
    //Do some stuff
}

//I am creating a delegate (pointer) to HandleSomethingHappened
//and adding it to SomethingHappened's list of "Event Handlers".
myObj.SomethingHappened += new MyEventHandler(HandleSomethingHappened);

//To raise the event within a method.
MyEventHandler("bar");

(*这是.NET中事件的关键,剥离了“魔力” - 事件真的只是一堆“形状”的方法列表,列表存储在事件所在的地方。事件是“引发的”,它实际上只是“遍历这个方法列表并调用每一个方法,并使用这些值作为参数”。分配事件处理程序只是将方法添加到此列表方法中的一种更漂亮,更简单的方法被称为)。


C#知道两个术语, delegateevent 。 我们从第一个开始。

代表

delegate是对方法的参考。 就像您可以创建对实例的引用一样:

MyClass instance = myFactory.GetInstance();

您可以使用委托来创建对方法的引用:

Action myMethod = myFactory.GetInstance;

现在您已经引用了一个方法,您可以通过引用调用该方法:

MyClass instance = myMethod();

但你为什么? 你也可以直接调用myFactory.GetInstance() 。 在这种情况下,你可以。 但是,有很多情况需要考虑您不希望应用程序的其余部分知道myFactory或直接调用myFactory.GetInstance()

一个明显的myFactory.GetInstance()是,如果您希望能够从一个中心位置(也称为工厂方法模式 )将myFactory.GetInstance()替换为myOfflineFakeFactory.GetInstance() )。

工厂方法模式

所以,如果你有一个TheOtherClass类,并且它需要使用myFactory.GetInstance() ,这就是没有委托的情况下代码的样子(你需要让TheOtherClass知道你的myFactory的类型):

TheOtherClass toc;
//...
toc.SetFactory(myFactory);


class TheOtherClass
{
   public void SetFactory(MyFactory factory)
   {
      // set here
   }

}

如果您使用委托,您不必公开我的工厂的类型:

TheOtherClass toc;
//...
Action factoryMethod = myFactory.GetInstance;
toc.SetFactoryMethod(factoryMethod);


class TheOtherClass
{
   public void SetFactoryMethod(Action factoryMethod)
   {
      // set here
   }

}

因此,您可以委派其他一些班级使用,而不必将您的类型暴露给他们。 你唯一要公开的是你的方法的签名(你有多少个参数等等)。

“我的方法签名”,我以前从哪里听过? 哦,是的,接口! 接口描述整个类的签名。 认为代表仅仅描述了一种方法的签名!

接口和委托之间的另一个很大的区别是,当你编写你的类时,你不必对C#说“这个方法实现了这种类型的委托”。 有了接口,你需要说“这个类实现了这种类型的接口”。

此外,委托引用可以(有一些限制,参见下文)引用多个方法(称为MulticastDelegate )。 这意味着当您调用委托时,将执行多个显式连接的方法。 对象引用始终只能引用一个对象。

MulticastDelegate的限制是(方法/委托)签名不应该有任何返回值( void ),并且签名中不使用关键字outref 。 显然,你不能调用返回一个数字的两个方法,并期望它们返回相同的数字。 一旦签名符合,委托自动成为MulticastDelegate

事件

事件只是属性(如get; set;属性到实例字段),这些属性向其他对象公开订阅。 但是,这些属性不支持get; set ;. 相反,他们支持添加;删除;

所以你可以有:

    Action myField;

    public event Action MyProperty
    {
        add { myField += value; }
        remove { myField -= value; }
    }

UI中的用法(WinForms)

所以,现在我们知道委托是对方法的引用,并且我们可以有一个事件让世界知道他们可以给我们的方法从我们的委托中引用,而我们是一个UI按钮,那么:我们可以询问任何对我是否被点击感兴趣的人,向我们注册他们的方法(通过我们公开的事件)。 我们可以使用所有提供给我们的方法,并由我们的代表引用它们。 然后,我们将等待,直到用户来到并点击该按钮,然后我们将有足够的理由调用委托。 而且由于委托引用了所有给予我们的方法,所有这些方法都会被调用。 我们不知道这些方法做了什么,也不知道哪个类实现了这些方法。 我们所关心的只是有人对我们被点击感兴趣,并且给了我们一个符合我们想要的签名的方法的参考。

Java的

像Java这样的语言没有代表。 他们改用接口。 他们这样做的方式是询问任何对'我们被点击'感兴趣的人来实现某个接口(使用我们可以调用的特定方法),然后给我们实现接口的整个实例。 我们可以保存实现此接口的所有对象的列表,并且可以在我们点击时调用他们的'我们可以调用的某些方法'。


这是一个代码示例,可以帮助:

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

namespace Event_Example
{
  // First we have to define a delegate that acts as a signature for the
  // function that is ultimately called when the event is triggered.
  // You will notice that the second parameter is of MyEventArgs type.
  // This object will contain information about the triggered event.

  public delegate void MyEventHandler(object source, MyEventArgs e);

  // This is a class which describes the event to the class that receives it.
  // An EventArgs class must always derive from System.EventArgs.

  public class MyEventArgs : EventArgs
  {
    private string EventInfo;

    public MyEventArgs(string Text) {
      EventInfo = Text;
    }

    public string GetInfo() {
      return EventInfo;
    }
  }

  // This next class is the one which contains an event and triggers it
  // once an action is performed. For example, lets trigger this event
  // once a variable is incremented over a particular value. Notice the
  // event uses the MyEventHandler delegate to create a signature
  // for the called function.

  public class MyClass
  {
    public event MyEventHandler OnMaximum;

    private int i;
    private int Maximum = 10;

    public int MyValue
    {
      get { return i; }
      set
      {
        if(value <= Maximum) {
          i = value;
        }
        else 
        {
          // To make sure we only trigger the event if a handler is present
          // we check the event to make sure it's not null.
          if(OnMaximum != null) {
            OnMaximum(this, new MyEventArgs("You've entered " +
              value.ToString() +
              ", but the maximum is " +
              Maximum.ToString()));
          }
        }
      }
    }
  }

  class Program
  {
    // This is the actual method that will be assigned to the event handler
    // within the above class. This is where we perform an action once the
    // event has been triggered.

    static void MaximumReached(object source, MyEventArgs e) {
      Console.WriteLine(e.GetInfo());
    }

    static void Main(string[] args) {
      // Now lets test the event contained in the above class.
      MyClass MyObject = new MyClass();
      MyObject.OnMaximum += new MyEventHandler(MaximumReached);
      for(int x = 0; x <= 15; x++) {
        MyObject.MyValue = x;
      }
      Console.ReadLine();
    }
  }
}
链接地址: http://www.djcxy.com/p/51485.html

上一篇: Understanding events and event handlers in C#

下一篇: Escaped curly brace in string.format gets lost. Possible C# bug?