通过lambda保持对象活着

时间:2013-12-22 20:39:24

标签: c# memory-management lambda garbage-collection

让我们检查以下场景(翻译事件):

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的实例,保持最终的目标。

1 个答案:

答案 0 :(得分:2)

对象保持活跃状态​​。它仍然是根深蒂固的#34;因为从按钮到对象的对象引用链包含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的{​​{1}}字段,以及一个EventHandlerobject的方法。

所以我们有以下参考资料:

  • RoutedEventArgs MyButton - &gt; Button
  • RoutedEventHandler - &gt; RoutedEventHandler
  • <>c__DisplayClass1 - &gt; <>c__DisplayClass1 eh
  • EventHandler eh - &gt; EventHandlerMyWindow

这里是嵌套子类OnButtonClicked的ILDasm输出:

MainWindow

当然,在我的示例中,无论如何,提供的事件处理程序都是root的,因为它位于 .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' 本身。但即使情况并非如此,也不会是GC&#39; d。

这意味着您可以获得实际需要的行为。但在许多应用程序中,它会导致内存泄漏。这就是编写代码以取消订阅事件或使用弱事件模式如此重要的原因。