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'