将一个对象的方法传递给另一个对象会使第一个对象保持活动

本文关键字:一个对象 活动 方法 | 更新日期: 2023-09-27 17:58:01

假设我有三个对象:"a"、"b"answers"c"。对象"a"answers"c"是长寿命的静态引用服务单例。对象"b"是短暂的,即没有静态引用使其保持活动状态。

现在假设对象"a"在其方法之一的范围内创建对象"b"的实例,例如

B b = new B();

进一步假设类B看起来像这样:

public B ()
{
    C.ActionList.Add ( SomeMethod );
}
void SomeMethod ()
{
...
}

现在,对象"b"能活多久?我的假设是,它超出了调用其构造函数的方法的范围;特别是,只要其方法仍在对象"c"的"ActionList"中。

这是正确的吗?如果不是,并且它被垃圾收集,那么当"c"运行其"ActionList"中的所有方法时会发生什么?

额外的问题:如果"b"上的方法没有命名,而是匿名的,并在构造函数中写如下:

public B ()
{
    C.ActionList.Add ( () => {
    ...
    } );
}

将一个对象的方法传递给另一个对象会使第一个对象保持活动

特别是,只要其方法仍在对象"c"的"ActionList"中。

是的,这是正确的。实例方法的委托会创建对实例本身的"硬引用",并使其保持活动状态。你的第二个问题与此无关。

请注意,这就是为什么事件订阅是.NET中"内存泄漏"的常见来源——从技术上讲,它们不会泄漏,但事件订阅基于委托,并且具有相同的行为,因此事件订阅包含对实例的引用。

如果"b"上的方法不是命名的,而是匿名的,并且是在构造函数的lambda中编写的,该怎么办?

这是一样的。如果lambda使用有问题的实例的状态,它将被编译器转换为具有对该实例的引用的类型的实例方法,并保持引用。(请注意,如果一些lambda表达式不依赖于任何闭包值、实例等,那么它们就有可能被转换为静态方法,在这种情况下,它就不会包含引用。)

在您的情况下,...的内容将决定这一点。如果您的表达式只是:() => { Console.WriteLine("Foo"); },那么它不需要关闭实例中的任何值,也不会使用有问题的实例,因此它不会包含引用。但是,如果执行() => { Console.WriteLine(this.Foo); },它将在引用this的类型上创建一个方法,并保持类实例的活动状态。