zoukankan      html  css  js  c++  java
  • 事件的解密

    在前面一篇中写到了委托,也说了委托是C#中很多特性的基础,这篇要讲的事件,就是建立在委托之上的。在C#1.0中,委托和事件是最重要的两个特性。

    1、什么是事件?

    事件设计到两类角色——事件发布者和事件订阅者。当某个事件发生后,事件发布者会发布消息;事件订阅者会接收到信息,并做出相应的处理,这就是事件的过程。

    2、使用事件

    2.1 定义事件

    在C#中定义事件和定义类的成员是很相似的,只要一个event关键字就可以了。比如:

    public event EventHandler birthday;

    其中event是关键字,而EventHandler是委托类型。

    所以可以把事件定义的结构总结为:访问修饰符 event 委托类型 事件名;其中委托类型可以是自定义的委托类型,也可以是.NET类库中预定义的委托类型EventHandler。

    2.2 订阅和取消事件

    事件订阅者需要订阅事件发布者发布的事件消息,以便在事件被触发式接收消息并做出相应处理。在C#中,可以使用“+=”来订阅事件,使用“-=”来取消订阅事件。

    public class Bridegroom
    {
      //自定义委托
      public delegate void MarryHandler(string msg);
      //使用自定义委托类型定义事件,事件名为MarryEvent
      public event MarryHandler MarryEvent;

      //发出事件
      public void OnMarriageComing(string msg)
      {
        //判断是否绑定了事件处理方法
        if(MarryEvent!=null)
        {
          //触发事件
          MarryEvent(msg);
        }
      }


      static void Main(string[] msg)
      {
        Bridegroom bridegroom=new Bridegroom();

        //实例化朋友对象
        Friend friend1=new Friend("张三");
        Friend friend2=new Friend("李四");
        Friend friend3=new Friend("王五");

        //使用“+=”来订阅事件
        bridegroom.MarryEvent+=new MarryHandler(friend1.SendMessage);
        bridgeroom.MarryEvent+=new MarryHandler(friend2.SendMessage);

        //发出通知,此时只有订阅了事件的对象才能收到通知
        bridegroom.OnMarriageComing("Friend,I Will Marry!!");
        Console.WriteLine("------------------------------------");

        //使用"-="来取消事件订阅,此时李四将收不到通知
        bridegroom.MarryEvent-=new MarryHandler(friend2.SendMessage);

        bridegroom.MarryEvent+=new MarryHandler(friend3.SendMessage);

        bridegroom.OnMarriageComing("Friend,I Will Marry!!");

        Console.ReadKey();
      }
    }

      public class Friend
      {
        public string Name;
        public Friend(string name)
        {
          Name=name;
        }
        //事件处理函数,该函数需要符合MarryHandler委托的定义
        public void SendMessage(string message)
        {
          Console.WriteLine(message);
          Console.WriteLine(this.Name+"收到了,到时候准时参加");
        }
      }

    值得注意的是,事件处理函数的定义需要与自定义的委托定义保持一致,即参数个数,参数类型和返回类型等需要与委托相同。

    除了使用自定义委托类型来定义事件外,还可以使用.NET类库中预定义的委托类型EventHandler来定义事件,需要注意它们的参数。

    public class Bridegroom
    {
      //使用.NET类库中的类型定义事件,事件名为MarryEvent
      public event EventHandler MarryEvent;

      //发出事件
      public void OnMarriageComing(string msg)
      {
        //判断是否绑定了事件处理方法
        if(MarryEvent!=null)
        {
          Console.WriteLine(msg);
          //触发事件
          MarryEvent(this,new EventArgs());
        }
      }


      static void Main(string[] msg)
      {
        Bridegroom bridegroom=new Bridegroom();

        //实例化朋友对象
        Friend friend1=new Friend("张三");
        Friend friend2=new Friend("李四");
        Friend friend3=new Friend("王五");

        //使用“+=”来订阅事件
        bridegroom.MarryEvent+=new MarryHandler(friend1.SendMessage);
        bridgeroom.MarryEvent+=new MarryHandler(friend2.SendMessage);

        //发出通知,此时只有订阅了事件的对象才能收到通知
        bridegroom.OnMarriageComing("Friend,I Will Marry!!");
        Console.WriteLine("------------------------------------");

        //使用"-="来取消事件订阅,此时李四将收不到通知
        bridegroom.MarryEvent-=new MarryHandler(friend2.SendMessage);

        bridegroom.MarryEvent+=new MarryHandler(friend3.SendMessage);

        bridegroom.OnMarriageComing("Friend,I Will Marry!!");

        Console.ReadKey();
      }
    }

    public class Friend
    {
      public string Name;
      public Friend(string name)
      {
        Name=name;
      }
      //事件处理函数,该函数需要符合MarryHandler委托的定义
      public void SendMessage(object s,EventArgs e)
      {
        Console.WriteLine(this.Name+"收到了,到时候准时参加");
      }
    }

    EventHandler是.NET类库中预定义的委托类型,用于处理不包含事件数据的事件。使用Reflector来查看EventHandler的具体定义:

    [Serializable, ComVisible(true), __DynamicallyInvokable]
    public delegate void EventHandler(object sender, EventArgs e);
    

    从定义中可以看出,该委托类型的返回类型为void,第一个参数sender负责保存触发事件对象的引用,其类型为object;第二个参数e负责保存事件数据。EventArgs类也是.NET类库中定义的类,它不保存任何数据,如果想在事件中包含事件数据,就必须使用EventArgs的派生类来实现。

    2.3 扩展EventArgs类

    上面说了,如果要在事件中包含事件数据,就必须使用EventArgs的派生类。具体的实现代码如下:

    public class MarryEventArgs:EventArgs
    {
      public string Message;
      public MarryEventArgs(string msg)
      {
        Message=msg;
      }
    }

    public class Bridegroom
    {
      //自定义委托类型,委托包含两个参数
      public delegate void MarryHandler(object sender,MarryEventArgs e);
      //使用自定义委托类型定义事件,事件名为MarryEvent
      public event MarryHandler MarryEvent;
      //发出事件
      public void OnMarriageComing(string msg)
      {
        //判断是否绑定了事件处理方法
        if(MarryEvent!=null)
        {
          //触发事件
          MarryEvent(this,new MarryEventArgs(msg));
        }
      }


      static void Main(string[] msg)
      {
        Bridegroom bridegroom=new Bridegroom();
        //实例化朋友对象
        Friend friend1=new Friend("张三");
        Friend friend2=new Friend("李四");
        Friend friend3=new Friend("王五");
        //使用“+=”来订阅事件
        bridegroom.MarryEvent+=new MarryHandler(friend1.SendMessage);
        bridgeroom.MarryEvent+=new MarryHandler(friend2.SendMessage);
        //发出通知,此时只有订阅了事件的对象才能收到通知
        bridegroom.OnMarriageComing("Friend,I Will Marry!!");
        Console.WriteLine("------------------------------------");
        //使用"-="来取消事件订阅,此时李四将收不到通知
        bridegroom.MarryEvent-=new MarryHandler(friend2.SendMessage);
        bridegroom.MarryEvent+=new MarryHandler(friend3.SendMessage);
        bridegroom.OnMarriageComing("Friend,I Will Marry!!");
        Console.ReadKey();
      }
    }

    public class Friend
    {
      public string Name;
      public Friend(string name)
      {
        Name=name;
      }
      //事件处理函数,该函数需要符合MarryHandler委托的定义
      public void SendMessage(object s,MarryEventArgs e)
      {
        Console.WriteLine(e.Message);
        Console.WriteLine(this.Name+"收到了,到时候准时参加");
      }
    }

    通过自定义MarryEventArgs事件类扩展了EventArgs类,此时MarryEventArgs带有一个名为Message的事件参数;然后在订阅对象的SendMessage方法中,通过e.Message的方式获得了事件数据,并把事件数据输出。

    3、事件的本质

    从以上的例子我们可以知道,事件是在委托的基础之上的。那么,它们到底有着什么样的关系呢,这个就必须通过Reflector来窥探了。

    简单的源代码:

    namespace 窥探事件本质
    {
      class Program
      {
        public delegate void MarryHanler(string msg);

        public event MarryHanler MarryEvent;
        static void Main(string[] args)
        {
        }
      }
    }

    Reflector反编译的结果:

    图1

    图2

    图3

    图4

    可以看出,C#事件被编译成包含两个公共方法的代码段,一个带有add_前缀,另一个带有remove_前缀,前缀后面是C#事件的名称。

    在add_方法中,通过调用了Delegate.Combine()方法来实现的(图3中红框的地方),Delegate.Combine()方法将多个委托组合为了一个多路广播委托。

    在remove_方法中,同样采用了Delegate.Remove()方法。

    由上面的四张图中可以总结出:

    C#的事件是一个特殊的多路广播委托,事件默认含有一个私有的委托类型变量(图2的红框),该变量用于保存对事件处理方法的引用,且该委托类型的变量为私有,只能从定义该事件的类中进行访问。

    从反编译的代码中可以看出跟我们学过的属性是相似的。但与事件不同,属性中定义了set访问和get访问器,两个访问器的本质就是以"get_"和"set_"为前缀的两个方法。属性用于对类中的私有字段进行访问,而C#事件也可以看作是“委托字段的属性”,因此可以通过事件来对私有的委托字段进行访问,这也是C#事件特性存在的原因。C#事件机制符合面向对象的封装特性,是代码更安全。

  • 相关阅读:
    编程之道——高内聚低耦合
    虚拟机的安装
    Tomcat的安装与配置
    Spring(十)--Advisor顾问
    Spring(九)--通知
    Spring(八)-- 代理设计模式
    Spring(七)--Spring JDBC
    Spring(六)--Spring配置文件之间的关系
    Spring(五)--autowire自动装配和spel
    Spring(四)--bean的属性赋值
  • 原文地址:https://www.cnblogs.com/Helius/p/5377384.html
Copyright © 2011-2022 走看看