对象将保持活跃。它仍然是“根”的,因为从按钮到包含eh 引用的方法的对象之间存在一系列对象引用。
根据 Simon Whitehead 对您的问题的评论,有趣的是编译器如何翻译此代码。对您的代码进行此扩展:
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
HookSpecificButton(this.MyButton, this.OnButtonClicked);
}
private void OnButtonClicked(object sender, EventArgs e)
{
}
public static void HookSpecificButton(Button specificButton, EventHandler eh)
{
specificButton.Click += (o, e) => eh(o, EventArgs.Empty);
}
}
Click 事件处理程序连接的那行实际上是以下的简写:
specificButton.Click += new RoutedEventHandler((o, e) => eh(o, EventArgs.Empty));
这说明您实际上是在创建一个RoutedEventHandler 委托对象。委托(用于非静态方法调用)包装对目标对象的引用和对该对象的实例方法的引用。
我们可以使用 ILDasm 检查 lambda 表达式会发生什么。我在MainWindow 中看到一个名为<>c__DisplayClass1 的嵌套类。此类有一个名为 eh 的类型为 EventHandler 的字段,以及一个采用 object 和 RoutedEventArgs 的方法。
所以我们有以下参考:
-
Button 我的按钮 -> RoutedEventHandler
-
RoutedEventHandler -> <>c__DisplayClass1
-
<>c__DisplayClass1 -> EventHandler 嗯
-
EventHandler eh -> MyWindow (OnButtonClicked)
这是 MainWindow 的嵌套子类的 ILDasm 输出:
.class auto ansi sealed nested private beforefieldinit '<>c__DisplayClass1'
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 )
.field public class [mscorlib]System.EventHandler eh
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method '<>c__DisplayClass1'::.ctor
.method public hidebysig instance void
'<HookSpecificButton>b__0'(object o,
class [PresentationCore]System.Windows.RoutedEventArgs e) cil managed
{
// Code size 18 (0x12)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldfld class [mscorlib]System.EventHandler ObjectLifetimeTest.MainWindow/'<>c__DisplayClass1'::eh
IL_0006: ldarg.1
IL_0007: ldsfld class [mscorlib]System.EventArgs [mscorlib]System.EventArgs::Empty
IL_000c: callvirt instance void [mscorlib]System.EventHandler::Invoke(object,
class [mscorlib]System.EventArgs)
IL_0011: ret
} // end of method '<>c__DisplayClass1'::'<HookSpecificButton>b__0'
} // end of class '<>c__DisplayClass1'
当然,在我的示例中,提供的事件处理程序无论如何都是根的,因为它在 Window 本身中。但即使不是这样,也不会被 GC 处理。
这意味着你得到了你真正想要的行为。但是在很多很多应用程序中,这是一个导致内存泄漏的问题。这就是编写代码取消订阅事件或使用弱事件模式如此重要的原因。