C# 通过lambda使对象保持活动状态

C# 通过lambda使对象保持活动状态,c#,memory-management,lambda,garbage-collection,C#,Memory Management,Lambda,Garbage Collection,让我们检查以下场景(转换事件): 代码的全部要点是将事件从一种类型转换为另一种类型:我不关心通过specificButton通过SpecificClick传递的数据,我希望附加到此事件常规EventHandler 我的问题如下eh包含对某个对象的方法的引用。如果没有对该对象的其他引用,lambda是否足以使该对象保持活动状态?这条链条是: specific按钮保持活动的EventHandler,该实例保持活动的(lambda)保持活动的(?)保持活动的EventHandler,该实例保持活动的最

让我们检查以下场景(转换事件):

代码的全部要点是将事件从一种类型转换为另一种类型:我不关心通过
specificButton
通过
SpecificClick
传递的数据,我希望附加到此事件常规EventHandler

我的问题如下
eh
包含对某个对象的方法的引用。如果没有对该对象的其他引用,lambda是否足以使该对象保持活动状态?这条链条是:

specific按钮
保持活动的
EventHandler
,该实例保持活动的
(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);
    }
}
连接
单击事件处理程序的行实际上是以下内容的简写:

        specificButton.Click += new RoutedEventHandler((o, e) => eh(o, EventArgs.Empty));
这说明您实际上正在创建一个
RoutedEventHandler
delegate对象。委托(对于非静态方法调用)包装对目标对象的引用和对该对象的实例方法的引用

我们可以使用ILDasm检查lambda表达式发生了什么。我在
main窗口
中看到一个嵌套类,名为
c\uu displayclas1
。此类有一个名为
eh
EventHandler
类型的字段,以及一个接受
对象和
路由事件目标的方法

因此,我们有以下参考资料:

  • 按钮
    MyButton->
    RoutedEventHandler
  • RoutedEventHandler
    ->
    c\u DisplayClass1
  • c\uu DisplayClass1
    ->
    EventHandler
    eh
  • EventHandler
    eh->
    MyWindow
    OnButtonClicked
以下是
main窗口的嵌套子类的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'
.class auto-ansi sealed嵌套private beforefieldinit'c\uu displayclas1'
扩展[mscorlib]System.Object
{
.custom instance void[mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()=(01 00)
.field公共类[mscorlib]System.EventHandler eh
.method公共隐藏显示特殊名称rtspecialname
实例void.ctor()cil托管
{
//代码大小7(0x7)
.maxstack 8
IL_0000:ldarg.0
IL_0001:调用实例void[mscorlib]System.Object::.ctor()
IL_0006:ret
}//方法“c_udisplayclas1”的结尾:.ctor
.method公共隐藏实例无效
“b__0”(对象o,
类[PresentationCore]System.Windows.RoutedEventArgs e)cil托管
{
//代码大小18(0x12)
.maxstack 8
IL_0000:ldarg.0
IL_0001:ldfld类[mscorlib]System.EventHandler ObjectLifetimeTest.MainWindow/'c_uDisplayClass1'::eh
IL_0006:ldarg.1
IL_0007:ldsfld类[mscorlib]System.EventArgs[mscorlib]System.EventArgs::Empty
IL_000c:callvirt实例void[mscorlib]System.EventHandler::Invoke(对象,
类[mscorlib]System.EventArgs)
IL_0011:ret
}//方法“c_uuDisplayClass1”的结尾::“b_uu0”
}//类“c\uu displayclas1”的结尾
当然,在我的示例中,所提供的事件处理程序无论如何都是根的,因为它位于
窗口中。但即使不是这样,也不会是GC'd


这意味着你得到了你真正想要的行为。但在许多应用程序中,这是导致内存泄漏的关键。这就是为什么编写代码来取消订阅事件或使用弱事件模式非常重要的原因。

是的,这就足够了。GC永远不会收集在此上下文中仍在使用的对象;“eh”包含对某个对象的方法的引用-您能详细说明一下吗?它捕获函数调用eh。
eh
是一个委托-方法引用的容器。但每个方法都必须在某些上下文中调用,因此委托包含对方法和某个实例的引用,该实例是该方法的所有者。该lambda在编译时提升为类。因此,实际的根对象是该类的一个实例。您能告诉我们您为什么关心它吗?通常GC所做的是无关的。
  .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'