c# – 用lambda保持对象活着

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

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.

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

点赞