让我们检查以下场景(翻译事件):
public void HookSpecificButton(SpecificButton specificButton, EventHandler eh)
{
specificButton.SpecificClick += (o, e) => eh(o, EventArgs.Empty);
}代码的全部目的是将事件从一种类型转换到另一种类型:我不关心specificButton通过SpecificClick传递的数据,我希望附加到这个事件的常规EventHandler。
我的问题如下。eh包含对某些对象的方法的引用。如果没有其他对该对象的引用,那么lambda就足以使该对象存活吗?这条链是:
specificButton保持EventHandler<SpecificData>的一个实例,该实例使(lambda)保持活动,它使(?)保持活力,这是EventHandler的一个实例,它使最终对象保持活力。
发布于 2014-03-15 21:39:37
物体将继续存活。它仍然是“根”的,因为从按钮到包含eh所引用的方法的对象有一个对象引用链。
根据Simon对您的问题的评论,编译器如何翻译这段代码是很有趣的。以您的代码展开为例:
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 MyButton -> RoutedEventHandlerRoutedEventHandler -> <>c__DisplayClass1<>c__DisplayClass1 -> EventHandler ehEventHandler eh -> MyWindow (OnButtonClicked)下面是嵌套的MainWindow子类的MainWindow输出
.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的。
这意味着你得到了你真正想要的行为。但在许多应用程序中,这是导致内存泄漏的一个缺陷。这就是为什么编写代码来取消订阅事件或使用弱事件模式如此重要的原因。
https://stackoverflow.com/questions/20733842
复制相似问题