以下代码直接取自MSDN introducing MVVM design pattern上文章随附的示例项目。我不太明白为什么委托看到除了null之外的'handler'的值。我的理解是为委托方法创建的闭包包含范围内的所有变量,这些变量在执行时已经初始化,并且由于在创建委托之后重新分配了'handler',所以闭包将包含'handler'设置为null
康斯坦丁
EventHandler handler = null;
handler = delegate
{
viewModel.RequestClose -= handler;
window.Close();
};
viewModel.RequestClose += handler;
答案 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