首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使物体活在羔羊身上

使物体活在羔羊身上
EN

Stack Overflow用户
提问于 2013-12-22 20:39:23
回答 1查看 596关注 0票数 4

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

代码语言:javascript
复制
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的一个实例,它使最终对象保持活力。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-03-15 21:39:37

物体将继续存活。它仍然是“根”的,因为从按钮到包含eh所引用的方法的对象有一个对象引用链。

根据Simon对您的问题的评论,编译器如何翻译这段代码是很有趣的。以您的代码展开为例:

代码语言:javascript
复制
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事件处理程序的行实际上是以下的缩写:

代码语言:javascript
复制
        specificButton.Click += new RoutedEventHandler((o, e) => eh(o, EventArgs.Empty));

这说明您实际上正在创建一个RoutedEventHandler委托对象。委托(用于非静态方法调用)封装对目标对象的引用和对该对象的实例方法的引用。

我们可以使用ILDasm检查lambda表达式发生了什么。我在MainWindow中看到一个名为<>c__DisplayClass1的嵌套类。这个类有一个名为eh (类型为EventHandler )的字段和一个接受objectRoutedEventArgs的方法。

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

  • Button MyButton -> RoutedEventHandler
  • RoutedEventHandler -> <>c__DisplayClass1
  • <>c__DisplayClass1 -> EventHandler eh
  • EventHandler eh -> MyWindow (OnButtonClicked)

下面是嵌套的MainWindow子类的MainWindow输出

代码语言:javascript
复制
  .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的。

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

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20733842

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档