CharlesChen's Technical Space

简单实用是我一直在软件开发追求的目标(I Focus on. Net technology, to make the greatest efforts to enjoy the best of life.)
Not the best, only better
  博客园  :: 首页  :: 联系 :: 订阅 订阅  :: 管理

通过Demo来细看C#事件的内在机制

Posted on 2010-07-08 11:03  Charles Chen  阅读(6810)  评论(25编辑  收藏  举报

    今天在博客园里面搜索了关于Delegate的文章,已经有很多好的博文了。最近我也研究了一下关于Delegate(委托)的背后的东西,也希望把研究的心得写出来供笔记参考。

我们可能会听别人谈起过C#事件,至少我曾经听同行这样描述过:

1、 C#事件是特殊的委托

2、 C#中使用委托模型来实现事件的。

3、 C#中的委托是一个引用类型,可以把它看成一个特殊的

 

所以我这里就通过C#事件来说Delegate(委托)。先看一段示例代码:

代码
public delegate void ProcessEventHandler(string name);
    
public class CharlesLog
    {
       
public event ProcessEventHandler processEvent;
       
public void ProcessHandler(string name)
       {
           
if (processEvent != null)
           {
               processEvent(name);
           }
           
else
           {
               processEvent 
= new ProcessEventHandler(DefaultHandler);
               processEvent(name);
           }
       }
        
public void DefaultHandler(string name)
        {
            Console.WriteLine(
"default Process!");
        }
    }
    
class Program
    {
        
static void Main(string[] args)
        {
            CharlesLog charlesLog 
= new CharlesLog();
            charlesLog.processEvent 
+= new ProcessEventHandler(CustomProcess); 
            charlesLog.ProcessHandler(
"CharlesChen");
        }
        
public static void CustomProcess(string name)
        {
            Console.WriteLine(
"custom Process!");
        }
}

 

 

 根据这个Demo,针对委托和事件而言,需要注意一些要点,我这里列举出来:

1、 上面的委托委托实例化用的是.Net 1.1的语法:

charlesLog.processEvent += new ProcessEventHandler(CustomProcess);

 

(注:)随着Net版本升级,委托实例化的语法简化,在2.0有匿名方法,以及3.0Lambda表达式更能符合人类语言。

2、 申明事件的语法是:修饰符” event <委托类型> 事件名,实际上是比申明委托实例多了一个event关键字而已。

3、 使用”+=”符合的时候会根据委托进行自动判断:

如果此时委托还没有实例化(null),它会自动用+=来对委托实例化。

如果委托已经实例化,它用+=来把函数注册委托指定的委托链上。

4、 事件(上面的processEvent)注册事件处理程序只能用+=或者-=,不能用=。否则会出现

CharlesLog.processEvent只能出现在+=-=的左边(CharlesLog类中使用除外)。

注意委托(delegate)是可以用=号来实例化

 

 

OK,上面提出的几点都是平常我们要注意的地方。如第4点,也许你会问为什么委托可以用=,而事件却不能用=号,而只能用+=-=呢?通过Reflactor工具来看一看event背后发生的事情:

public event ProcessEventHandler processEvent;

 

通过C#编译器会编译成:

 

可以看到,尽管这里定义的processEvent申明为public,但是实际上processEvent会被编译成私有字段(到这里你应该晓得为什么对于事件你不能在外部用”=”,但是对于内部可以用”=”)。因为它根本不允许在CharlesLog的外面已赋值的方式来访问。我们进一步看一下:

 

代码
private ProcessEventHandler processEvent;

        [MethodImpl(MethodImplOptions.Synchronized)]
        
public void add_processEvent(ProcessEventHandler value)
        {
            
this.processEvent = (ProcessEventHandler)Delegate.Combine(this.processEvent, value);
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        
public void remove_processEvent(ProcessEventHandler value)
        {
            
this.processEvent = (ProcessEventHandler)Delegate.Remove(this.processEvent, value);
    }

 

OK,现在已经明确了,processEvent确实是ProcessEventHandler类型的委托,只不过不管是申明为public,它总是被申明为Private。另外,它还增加了两个方法,分别是add_processEventremove_processEvent方法,这两个方法分别用于注册委托类型的方法和取消注册。实际上也就是:“+=”对应add_processEvent,-=”对应remove_processEvent而这两个方法的访问限制取决于申明事件的访问修饰符。

我们也可以看到在add_processEvent()方法内部,实际上是调用了System.DelegateCombine()的静态方法,这个方法用于将当前的变量添加到委托链中。

我们前面说:委托实际上是一个类。也就是说当编译器遇到

public delegate void ProcessEventHandler(string name)

 

 代码的时候,会生成下面这样一个类:

代码
public sealed class ProcessEventHandler : System.MulticastDelegate
    {
        [MethodImpl(
0, MethodCodeType = MethodCodeType.Runtime)]
        
public ProcessEventHandler(object @object, IntPtr method);
        [MethodImpl(
0, MethodCodeType = MethodCodeType.Runtime)]
        
public virtual IAsyncResult BeginInvoke(string name, AsyncCallback callback, object @object);
        [MethodImpl(
0, MethodCodeType = MethodCodeType.Runtime)]
        
public virtual void EndInvoke(IAsyncResult result);
        [MethodImpl(
0, MethodCodeType = MethodCodeType.Runtime)]
        
public virtual void Invoke(string name);
 }

 

 如下图所示:

 

那我们可能还会想:那根据这委托生成的类,我们在Main函数中实注册事件的时候是怎样调用ProcessEventHandler委托的呢?如果要分析的话,我们可以根据IL代码来看到基本的过程:

 

上面这幅图IL图只说明了我们把CustomProcess加到委托链中,那么当我们调用委托时候,它是根据什么去调用委托链中的方法呢?下面我们看一下CharlesLog中的ProcessHandler()方法。

OK,上面这两幅图就是前面代码通过C#编译器产生的IL代码样例,现在我们可以清楚的理解最前面说的:

 

1、 C#事件是特殊的委托

2、 C#中使用委托模型来实现事件的。

3、 C#中的委托是一个引用类型,可以把它看成一个特殊的

 

OK,本文就写到这里。主要是通过Demo去了解了C#事件背后发生的事情。关于C#委托与事件的详细介绍可以从博客园中其他好文获取:

1、浅谈C#委托和事件

2、.NET中委托写法的演变谈开去

3、C# 中的委托和事件

4、委托揭秘 

版权说明

  本文属学习笔记,欢迎转载且注明文章出处,其版权归作者和博客园共有。  

  作      者:Charles Chen

 文章出处:http://charles2008.cnblogs.com/  或  http://www.cnblogs.com/