【问题标题】:does passing a method of one object to another object keep the first object alive?将一个对象的方法传递给另一个对象是否会使第一个对象保持活动状态?
【发布时间】:2014-05-26 02:13:52
【问题描述】:

假设我有三个对象:“a”、“b”和“c”。 对象“a”和“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# delegates scope action object-lifetime


    【解决方案1】:

    具体来说,只要它的方法仍在对象'c'的'ActionList'中。

    是的,没错。实例方法的委托会创建对实例本身的“硬引用”,并将使其保持活动状态。您的第二个问题不相关。

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

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

    这是一样的。如果 lambda 使用相关实例的状态,它将被编译器转换为具有对该实例的引用的类型的实例方法,并持有一个引用。 (请注意,如果某些 lambda 表达式不依赖任何封闭的值、实例等,则可以将其转换为静态方法,在这种情况下,它不会持有引用。)

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

    【讨论】:

    • +1 “这就是为什么事件订阅是 .NET 和其他语言中“内存泄漏”的常见来源。在 JavaScript 兴起之初,浏览器会由于未释放过时的事件处理程序而导致内存泄漏
    猜你喜欢
    • 2018-07-28
    • 2021-07-26
    • 2015-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-02
    • 1970-01-01
    • 2013-01-12
    相关资源
    最近更新 更多