none
关于委托的问题 RRS feed

  • 问题

  • 我写了两段代码:

    第一段:

    namespace TestConsoleApplication1
    {
      class Program
      {
        static void Main(string[] args)
        {
          Program p = new Program();
          p.eventHandler += p.ClickMeFirst;
          p.eventHandler += p.ClickMeSecond;
    
          p.eventHandler(new object());
    
          Console.Read();
        }
    
        OnClickMeEventHandler eventHandler;
    
        public void ClickMeFirst(object sender)
        {
          Console.WriteLine("ClickMe First!");
        }
    
        public void ClickMeSecond(object sneder)
        {
          Console.WriteLine("ClickMe Second!");
        }
      }
    
      delegate void OnClickMeEventHandler(object sender);
    }
    

    第二段:

    namespace TestConsoleApplication1
    {
      class Program
      {
        static void Main(string[] args)
        {
          Program p = new Program();
          OnClickMeEventHandler eventHandler;
          eventHandler += p.ClickMeFirst;
          eventHandler += p.ClickMeSecond;
    
          eventHandler(new object());
    
          Console.Read();
        }
    
        public void ClickMeFirst(object sender)
        {
          Console.WriteLine("ClickMe First!");
        }
    
        public void ClickMeSecond(object sneder)
        {
          Console.WriteLine("ClickMe Second!");
        }
      }
    
      delegate void OnClickMeEventHandler(object sender);
    }
    


    这两段代码几乎是一样的,区别在于第二段代码将  OnClickMeEventHandler eventHandler;  这一句由Program类的内部移到了Main函数内。

    结果却是大相径庭,第一段代码正常运行,第二段代码编译错误:

    错误 1 使用了未赋值的局部变量“eventHandler” 

     

    于是我得出结论,在类内部声明一个delegate作为局部变量和在某函数内部声明一个delegate相比,该delegate初始化方式或者说步骤应该是有区别的。

    请问是否是这样?如果是,那么这区别是什么?为什么有这样的区别?如果不是,那为什么第二段代码编译出错呢?

    谢谢!

    2011年6月12日 1:45

答案

全部回复

  • 你好:)

    “委托”是一种特殊的类型。它定义以后必须和普通类型一样,必须“先声明,后使用“。

    第一个例子中,显然class Program的变量eventHandler的类型是委托,且依附于p的实例上,那么p实例化以后就可以直接使用了(使用+=多路委托)。

    至于第二个例子——你虽然直接声明了临时变量,但是在Main函数中,直接使用多路委托+=是不可以的,因此只能使用以下形式——

    static void Main(string[] args)
            {
                OnClickMeEventHandler eventHandler;
                Program p = new Program();
                eventHandler = new OnClickMeEventHandler(p.ClickMeFirst);
                eventHandler += new OnClickMeEventHandler(p.ClickMeSecond);

                eventHandler(new object());

                Console.Read();
            }


    如果你有其它意见或私下交流,请发送邮件到:maledong@qq.com;或者请QQ我
    下载MSDN桌面工具(Vista,Win7)
    下载Technet桌面小工具(Vista,Win7)
    慈善点击,点击此处
    2011年6月12日 3:01
    版主
  • 嗯,你说的对,不能使用 += 是由于eventHandler没有初始化引起的。

    不过,如果我手动对eventHandler进行初始化,就必须写成 eventHandler = new OnClickMeEventHandler(p.ClickMeFirst) 这样的形式。

    而不能写成 OnClickMeEventHandler eventHandler = new OnClickMeEventHandler() 这样的形式。

    这样会有一个编译错误“错误 1 “TestConsoleApplication1.OnClickMeEventHandler”不包含采用“0”参数的构造函数 “”

    那么问题又来了,我的第一段代码中Program类中的OnClickMeEventHandler在Program实例化为P的时候是如何进行初始化的?

    2011年6月13日 1:26
  • 问的好!

    “+=”在委托中——如果这个委托是一个类的一部分(是类的变量),那么在使用“+=”的时候,其实是自动通过编译器“语法糖”功能自动调用了Delegate.Combine函数,我用Reflector反编译了第一个程序:

    private static void Main(string[] args)
        {
            Program p;
            new Program { eventHandler = (OnClickMeEventHandler) Delegate.Combine(p.eventHandler, new OnClickMeEventHandler(p.ClickMeFirst)), eventHandler = (OnClickMeEventHandler) Delegate.Combine(p.eventHandler, new OnClickMeEventHandler(p.ClickMeSecond)) }.eventHandler(new object());
            Console.Read();
        }

    显然,使用Combine函数把多个委托串接起来,自然达到“多米诺骨牌”的串接效应。


    但是,如果是第二个程序,那么直接使用的话——因为单独声明一个临时的委托类型,系统不会特殊处理,只会按照普通变量处理,没有初始化自然出错了。


    如果你有其它意见或私下交流,请发送邮件到:maledong@qq.com;或者请QQ我
    下载MSDN桌面工具(Vista,Win7)
    下载Technet桌面小工具(Vista,Win7)
    慈善点击,点击此处
    2011年6月13日 1:48
    版主