匿名委托关闭(或者为什么这样做)?

时间:2010-09-19 19:25:45

标签: c# .net-4.0

以下代码直接取自MSDN introducing MVVM design pattern上文章随附的示例项目。我不太明白为什么委托看到除了null之外的'handler'的值。我的理解是为委托方法创建的闭包包含范围内的所有变量,这些变量在执行时已经初始化,并且由于在创建委托之后重新分配了'handler',所以闭包将包含'handler'设置为null

康斯坦丁


EventHandler handler = null;
handler = delegate
{
    viewModel.RequestClose -= handler;
    window.Close();
};
viewModel.RequestClose += handler;

4 个答案:

答案 0 :(得分:4)

委托捕获变量handler,而不是变量的内容。


当您查看C#编译器编译代码的内容时,例如使用Reflector,会变得更加清晰。您的代码大致编译为:

class MyAnonymousDelegate
{
    public ... viewModel;
    public ... window;
    public EventHandler handler;

    public void DoIt(object sender, EventArgs e)
    {
        this.viewModel.RequestClose -= this.handler;
        this.window.Close();
    }
}

var mad = new MyAnonymousDelegate();
mad.viewModel = viewModel;
mad.window = window;
mad.handler = null;

mad.handler = new EventHandler(mad.DoIt);

viewModel.RequestClose += mad.handler;

答案 1 :(得分:2)

像这样写:

EventHandler handler = delegate
{
    viewModel.RequestClose -= handler;
    window.Close();
};
viewModel.RequestClose += handler;

获取错误CS0165:使用未分配的局部变量'handler'。

可疑诊断,实际上并未取消分配。实现匿名方法的隐藏类实际上是在分配捕获的'handler'值之前创建的。但是,在C#编译器中实现这一点并不容易。边缘情况。

答案 2 :(得分:1)

闭包不复制变量,它维护一个引用。创建委托并设置处理程序后,将更新此更改。

答案 3 :(得分:1)

在分配之前初始化处理程序:

       static void Main(string[] args)
    {

        EventHandler handler = null;

        handler = delegate
        {
            AppDomain.CurrentDomain.ProcessExit -= handler;

        };
        AppDomain.CurrentDomain.ProcessExit += handler;
    }

编译为:

.method private hidebysig static void  Main(string[] args) cil managed
{
   .entrypoint
   // Code size       51 (0x33)
   .maxstack  4
   .locals init ([0] class ConsoleApplication1.Program/'c__DisplayClass1'    'CS$8__locals2')
   IL_0000:  newobj     instance void    ConsoleApplication1.Program/'c__DisplayClass1'::.ctor()
   IL_0005:  stloc.0
  IL_0006:  nop
  IL_0007:  ldloc.0
  IL_0008:  ldnull
  IL_0009:  stfld      class [mscorlib]System.EventHandler ConsoleApplication1.Program/'c__DisplayClass1'::'handler'
  IL_000e:  ldloc.0
  IL_000f:  ldloc.0
  IL_0010:  ldftn      instance void ConsoleApplication1.Program/'c__DisplayClass1'::'b__0'(object,
                                                                                                    class [mscorlib]System.EventArgs)
  IL_0016:  newobj     instance void [mscorlib]System.EventHandler::.ctor(object,
                                                                          native int)
  IL_001b:  stfld      class [mscorlib]System.EventHandler ConsoleApplication1.Program/'c__DisplayClass1'::'handler'
  IL_0020:  call       class [mscorlib]System.AppDomain [mscorlib]System.AppDomain::get_CurrentDomain()
  IL_0025:  ldloc.0
  IL_0026:  ldfld      class [mscorlib]System.EventHandler ConsoleApplication1.Program/'c__DisplayClass1'::'handler'
  IL_002b:  callvirt   instance void [mscorlib]System.AppDomain::add_ProcessExit(class [mscorlib]System.EventHandler)
  IL_0030:  nop
  IL_0031:  nop
  IL_0032:  ret
} // end of method Program::Main