开发者

Anonymous delegate closure (or why does this work)?

The code below is taken directly from t开发者_高级运维he sample project accompanying the article on MSDN introducing MVVM design pattern. I do not quite understand why the delegate sees the value of 'handler' other than null. My understanding was that the closure created for the delegate method contains all variables in scope that have been initialized up to this point in the execution, and since 'handler' is reassigned after the delegate is created the closure will contain 'handler' set to null.

Konstantin


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


The delegate captures the variable handler, not the contents of the variable.


It becomes more clear when you look at what the C# compiler compiles your code to, e.g., using Reflector. Your code is compiled roughly to this:

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;


Write it like this:

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

To get error CS0165: Use of unassigned local variable 'handler'.

Questionable diagnostic, it isn't actually unassigned. The hidden class that implements the anonymous method does in fact get created before the captured value of 'handler' is assigned. That cannot be easy to implement in the C# compiler though. Edge case.


A closure doesn't copy the variables, it maintains a reference. When the delegate is created and the handler is set, this change is updated.


The handler is initialised before asigning:

       static void Main(string[] args)
    {

        EventHandler handler = null;

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

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

Compiles to:

.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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜