让我们检查以下场景(翻译事件):
public void HookSpecificButton(SpecificButton specificButton, EventHandler eh)
{
specificButton.SpecificClick += (o, e) => eh(o, EventArgs.Empty);
}
代码的重点是将事件从一种类型转换为另一种类型:我不关心specificButton通过SpecificClick传递的数据,我想要附加到这个事件的常规EventHandler.
我的问题如下.呃包含对某个对象方法的引用.如果没有其他对该对象的引用,lambda是否足以让该对象保持活动状态?链是:
specificButton保持活动EventHandler< SpecificData>的实例,它保持活动(lambda),它保持活动(?)EventHandler的一个实例,它保持最终对象的活着.
最佳答案 该物体将保持活力.它仍然是“根”,因为从按钮到包含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类型的字段,以及一个获取对象和RoutedEventArgs的方法.
所以我们有以下参考:
>按钮MyButton – > RoutedEventHandler
> RoutedEventHandler – > <> c__DisplayClass1
><> c__DisplayClass1 – > EventHandler呃
> EventHandler呃 – > 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'
当然,在我的例子中,提供的事件处理程序无论如何都是root的,因为它位于Window本身.但即使不是这样,也不会是GC.
这意味着您可以获得实际需要的行为.但在许多应用程序中,它会导致内存泄漏.这就是编写代码以取消订阅事件或使用弱事件模式如此重要的原因.