也探讨一下关于Event的具体案例或影响?

2026-05-27 04:041阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1612个文字,预计阅读时间需要7分钟。

也探讨一下关于Event的具体案例或影响?

最近园子里发生了一些事件(Event)的讨论,我也来凑个热闹,谈谈我对事件的一些粗浅认识。

本文不涉及设计模式(观察者模式),仅从运行时的角度来分析事件这个对象。

最近园子里发表了一些讨论“事件(Event)”的文章,我也来凑个热闹,谈谈我对事件的一些粗浅的认识。本文不谈设计模式(观察者模式),只从运行时的角度来分析事件这个对象到底是个什么东西,它有那么神秘吗?为了更好的分析事件,本文将会编写一些例子来模拟事件的订阅机制。本文对事件的分析可以概括为下面三句话:

  • 事件本质上是一个MulticastDelegate对象;
  • MulticastDelegate对象是多个Delegate对象的链表;
  • Delegate = Object + MethodInfo,委托的执行最终通过反射来完成
一、Delegate = Object + MethodInfo

其实你完全可以通过Reflector这样的工具来看Delegate类型是如何定义的。在这里,我们只关注Delegate本质的东西,即Delegate最终是如果执行的。为此,我创建了下面一个简单的MyDelegate类型来模拟Delegate。

1: public class MyDelegate

2: {

3:

4: public object Target { get; private set; }

5: public MethodInfo Method { get; private set; }

6:

7: public MyDelegate(object target, MethodInfo method)

8: {

9: this.Target = target;

10: this.Method = method;

11: }

12:

13: public virtual void Invoke(params object[] args)

14: {

15: this.Method.Invoke(this.Target, args);

16: }

17: }

从上面的定义可以看到,MyDelegate只有两个属性:Object类型的Target和MethodInfo类型的Method。委托的执行通过需方法Invoke完成,具体来说,最终的执行通过反射的方式调用Method的Invoke方法完成。

二、MulticastDelegate对象多个Delegate对象的链表

其实我们平时讲的委托,并不是一个单个的Delegate对象,实际上是一个委托链,这样一个委托链通过MulticastDelegate定义。由于定义也相对复杂,我们同样通过定义模拟类型来反映其本质的东西。为此,我创建了如下一个MyMulticastDelegate类型。

1: public class MyMulticastDelegate : MyDelegate

2: {

3: public MyMulticastDelegate Next { get; set; }

4:

5: public MyMulticastDelegate(object target, MethodInfo method)

6: : base(target, method)

7: { }

8:

9: public override void Invoke(params object[] args)

10: {

11: base.Invoke(args);

12: if (null != Next)

13: {

14: this.Next.Invoke(args);

15: }

16: }

17: }

MyMulticastDelegate继承自上面定义的MyDelegate类型,在此基础上定义了一个额外的属性Next,代表委托链中当前委托对象的下一个委托。最后,Invoke方法被重写:按照委托链的顺序依次执行每一个委托对象。

三、事件本质上是一个MulticastDelegate对象

我们使用的事件一般通过EventHandler或者System.EventHandler<TEventArgs>表示,其本质来时一个通过MulticastDelegate对象表示的委托链。事件注册本质就是将另外一个委托(链)连到当前委托链上。下面定义的类型MyEventHandler模拟了事件的实现。

1: public class MyEventHandler : MyMulticastDelegate

2: {

3: public MyEventHandler(object target, MethodInfo method)

4: : base(target, method)

5: { }

6:

7: public void Fire(object sender,EventArgs args)

8: {

9: this.Invoke(sender,args);

10: }

11:

12: public static MyEventHandler operator +(MyEventHandler current, MyEventHandler next)

13: {

14: if (null == current)

15: {

16: return next;

17: }

18:

19: MyMulticastDelegate terminator = current;

20: while (null != terminator.Next)

21: {

22: terminator = terminator.Next;

23: }

24:

25: terminator.Next = next;

26: return current;

27: }

28:

29: public static implicit operator MyEventHandler(EventHandler eventHandler)

30: {

31: return new MyEventHandler(eventHandler.Target, eventHandler.Method);

32: }

33: }

事件一般通过+=操作符进行注册,其本质就是将两个委托链相连。为此,在MyEventHandler中,我也重载了操作符+。事件的触发被定义在Fire方法中,其实现就是调用MyMulticastDelegate的Invoke方法。此外,我还定义一个隐式类型转换操作符,将EventHandler转对象化成MyEventHandler类型。

四、事件的订阅

现在我通过一个具体的例子来说明通过上面定义的MyEventHandler来模拟具体的事件注册和触发。在这里,我们模拟的是Button的Click事件,为此我采用标准的事件编程方式定义了如下一个Button类型。MyEventHandler类型的Click属性代表事件本身,Click操作的触发通过执行PerformClick方法完成。进一步地,Click操作的处理实现在虚方法OnClick中,其本质就是调用MyEventHandler的Fire方法。

1: public class Button

2: {

3: public string Id { get; private set; }

4: public MyEventHandler Click { get; set; }

5:

6: public Button(string id)

7: {

8: this.Id = id;

9: }

10:

11: protected virtual void OnClick(EventArgs args)

12: {

13: if(null != this.Click)

14: {

15: this.Click.Fire(this, args);

16: }

17: }

18:

19: public void PerformClick()

20: {

21: this.OnClick(EventArgs.Empty);

22: }

23: }

接下来,我们创建另一个模拟订阅Button对象的Click事件的类型,这样一个简单的类型Foo定义如下。当订阅的Click事件触发之后,会回调DoSomethingOnceClick方法,方法会在控制台上输出一段文字。

1: public class Foo

2: {

3: public void DoSomethingOnceClick(object sender, EventArgs args)

4: {

5: Button btn = sender as Button;

6: if(null != btn)

7: {

8: Console.WriteLine("Click {0}", btn.Id);

9: }

10: }

11: }

那么最终的事件订阅和触发编写在下面代码中:在创建的Button对象中,进行了6次相同的事件注册,最终通过PerformClick方法触发事件。由于在MyEventHandler定义一个从EventHandler到MyEventHandler类型的隐式转换操作符,所以我们进行事件注册和传统的方式别无二致。

1: class Program

2: {

3: static void Main(string[] args)

4: {

5: var btn1 = new Button("Button1");

6: var foo = new Foo();

7: btn1.Click += new EventHandler(foo.DoSomethingOnceClick);

8: btn1.Click += new EventHandler(foo.DoSomethingOnceClick);

9: btn1.Click += new EventHandler(foo.DoSomethingOnceClick);

10: btn1.Click += new EventHandler(foo.DoSomethingOnceClick);

11: btn1.Click += new EventHandler(foo.DoSomethingOnceClick);

12: btn1.Click += new EventHandler(foo.DoSomethingOnceClick);

13: btn1.PerformClick();

14: }

15: }

下面是最后的输出结果:

也探讨一下关于Event的具体案例或影响?

Click Button1

Click Button1

Click Button1

Click Button1

Click Button1

Click Button1

本文提供的例子,你可以通过这里下载,关于事件相关的内容,我还有一篇相关的文章《如何编写没有Try/Catch的程序》,仅供参考。

本文共计1612个文字,预计阅读时间需要7分钟。

也探讨一下关于Event的具体案例或影响?

最近园子里发生了一些事件(Event)的讨论,我也来凑个热闹,谈谈我对事件的一些粗浅认识。

本文不涉及设计模式(观察者模式),仅从运行时的角度来分析事件这个对象。

最近园子里发表了一些讨论“事件(Event)”的文章,我也来凑个热闹,谈谈我对事件的一些粗浅的认识。本文不谈设计模式(观察者模式),只从运行时的角度来分析事件这个对象到底是个什么东西,它有那么神秘吗?为了更好的分析事件,本文将会编写一些例子来模拟事件的订阅机制。本文对事件的分析可以概括为下面三句话:

  • 事件本质上是一个MulticastDelegate对象;
  • MulticastDelegate对象是多个Delegate对象的链表;
  • Delegate = Object + MethodInfo,委托的执行最终通过反射来完成
一、Delegate = Object + MethodInfo

其实你完全可以通过Reflector这样的工具来看Delegate类型是如何定义的。在这里,我们只关注Delegate本质的东西,即Delegate最终是如果执行的。为此,我创建了下面一个简单的MyDelegate类型来模拟Delegate。

1: public class MyDelegate

2: {

3:

4: public object Target { get; private set; }

5: public MethodInfo Method { get; private set; }

6:

7: public MyDelegate(object target, MethodInfo method)

8: {

9: this.Target = target;

10: this.Method = method;

11: }

12:

13: public virtual void Invoke(params object[] args)

14: {

15: this.Method.Invoke(this.Target, args);

16: }

17: }

从上面的定义可以看到,MyDelegate只有两个属性:Object类型的Target和MethodInfo类型的Method。委托的执行通过需方法Invoke完成,具体来说,最终的执行通过反射的方式调用Method的Invoke方法完成。

二、MulticastDelegate对象多个Delegate对象的链表

其实我们平时讲的委托,并不是一个单个的Delegate对象,实际上是一个委托链,这样一个委托链通过MulticastDelegate定义。由于定义也相对复杂,我们同样通过定义模拟类型来反映其本质的东西。为此,我创建了如下一个MyMulticastDelegate类型。

1: public class MyMulticastDelegate : MyDelegate

2: {

3: public MyMulticastDelegate Next { get; set; }

4:

5: public MyMulticastDelegate(object target, MethodInfo method)

6: : base(target, method)

7: { }

8:

9: public override void Invoke(params object[] args)

10: {

11: base.Invoke(args);

12: if (null != Next)

13: {

14: this.Next.Invoke(args);

15: }

16: }

17: }

MyMulticastDelegate继承自上面定义的MyDelegate类型,在此基础上定义了一个额外的属性Next,代表委托链中当前委托对象的下一个委托。最后,Invoke方法被重写:按照委托链的顺序依次执行每一个委托对象。

三、事件本质上是一个MulticastDelegate对象

我们使用的事件一般通过EventHandler或者System.EventHandler<TEventArgs>表示,其本质来时一个通过MulticastDelegate对象表示的委托链。事件注册本质就是将另外一个委托(链)连到当前委托链上。下面定义的类型MyEventHandler模拟了事件的实现。

1: public class MyEventHandler : MyMulticastDelegate

2: {

3: public MyEventHandler(object target, MethodInfo method)

4: : base(target, method)

5: { }

6:

7: public void Fire(object sender,EventArgs args)

8: {

9: this.Invoke(sender,args);

10: }

11:

12: public static MyEventHandler operator +(MyEventHandler current, MyEventHandler next)

13: {

14: if (null == current)

15: {

16: return next;

17: }

18:

19: MyMulticastDelegate terminator = current;

20: while (null != terminator.Next)

21: {

22: terminator = terminator.Next;

23: }

24:

25: terminator.Next = next;

26: return current;

27: }

28:

29: public static implicit operator MyEventHandler(EventHandler eventHandler)

30: {

31: return new MyEventHandler(eventHandler.Target, eventHandler.Method);

32: }

33: }

事件一般通过+=操作符进行注册,其本质就是将两个委托链相连。为此,在MyEventHandler中,我也重载了操作符+。事件的触发被定义在Fire方法中,其实现就是调用MyMulticastDelegate的Invoke方法。此外,我还定义一个隐式类型转换操作符,将EventHandler转对象化成MyEventHandler类型。

四、事件的订阅

现在我通过一个具体的例子来说明通过上面定义的MyEventHandler来模拟具体的事件注册和触发。在这里,我们模拟的是Button的Click事件,为此我采用标准的事件编程方式定义了如下一个Button类型。MyEventHandler类型的Click属性代表事件本身,Click操作的触发通过执行PerformClick方法完成。进一步地,Click操作的处理实现在虚方法OnClick中,其本质就是调用MyEventHandler的Fire方法。

1: public class Button

2: {

3: public string Id { get; private set; }

4: public MyEventHandler Click { get; set; }

5:

6: public Button(string id)

7: {

8: this.Id = id;

9: }

10:

11: protected virtual void OnClick(EventArgs args)

12: {

13: if(null != this.Click)

14: {

15: this.Click.Fire(this, args);

16: }

17: }

18:

19: public void PerformClick()

20: {

21: this.OnClick(EventArgs.Empty);

22: }

23: }

接下来,我们创建另一个模拟订阅Button对象的Click事件的类型,这样一个简单的类型Foo定义如下。当订阅的Click事件触发之后,会回调DoSomethingOnceClick方法,方法会在控制台上输出一段文字。

1: public class Foo

2: {

3: public void DoSomethingOnceClick(object sender, EventArgs args)

4: {

5: Button btn = sender as Button;

6: if(null != btn)

7: {

8: Console.WriteLine("Click {0}", btn.Id);

9: }

10: }

11: }

那么最终的事件订阅和触发编写在下面代码中:在创建的Button对象中,进行了6次相同的事件注册,最终通过PerformClick方法触发事件。由于在MyEventHandler定义一个从EventHandler到MyEventHandler类型的隐式转换操作符,所以我们进行事件注册和传统的方式别无二致。

1: class Program

2: {

3: static void Main(string[] args)

4: {

5: var btn1 = new Button("Button1");

6: var foo = new Foo();

7: btn1.Click += new EventHandler(foo.DoSomethingOnceClick);

8: btn1.Click += new EventHandler(foo.DoSomethingOnceClick);

9: btn1.Click += new EventHandler(foo.DoSomethingOnceClick);

10: btn1.Click += new EventHandler(foo.DoSomethingOnceClick);

11: btn1.Click += new EventHandler(foo.DoSomethingOnceClick);

12: btn1.Click += new EventHandler(foo.DoSomethingOnceClick);

13: btn1.PerformClick();

14: }

15: }

下面是最后的输出结果:

也探讨一下关于Event的具体案例或影响?

Click Button1

Click Button1

Click Button1

Click Button1

Click Button1

Click Button1

本文提供的例子,你可以通过这里下载,关于事件相关的内容,我还有一篇相关的文章《如何编写没有Try/Catch的程序》,仅供参考。