【问题标题】:Accessing field of out of scope class instance访问范围外类实例的字段
【发布时间】:2018-11-21 12:40:10
【问题描述】:

让我们考虑这个类:

public class A<T>
{
    private bool _flag;

    public Func<T> Function { get; set; } = () => {_flag = true; return _flag; }
}

现在,让我们假设 Function 属性以某种方式访问​​ _flag 字段,同时在其主体中进行读取和写入。然后,如果我像这样使用 A 类:

public Func<T> SomeFunction()
{
    var instance = new A();
    return instance.Function;
}

我的问题是真正发生了什么,因为我最初假设该实例将在 SomeFunctions 返回时由 GC 处理,这意味着 _flag 将不复存在,并且该函数将在最终从某个地方调用时尝试访问不存在的字段,但事实并非如此。该代码似乎有效。该字段是否以某种方式保存在闭包中?

感谢您的澄清。

【问题讨论】:

  • 更不用说私有修饰符了,一个函数如何访问一个类的实例成员,除非它可以访问实例本身(通过参数、闭包或其他类成员等)?显然,在这种情况下,它与您的示例中 var instance = new A(); 创建的实例无关。
  • @DmytroMukalov 为了更清晰,我编辑了 A 类 :)
  • 在这种情况下,实例将被保留,因为它被其他对象引用。这不是 C++,对象不是在离开作用域时而是在没有其他人引用它时才成为 GC 的主题。

标签: c# scope garbage-collection closures


【解决方案1】:

为了简单起见,我将使用 Dummy 类(而不是您的通用 A):

class Dummy
{
    public int i = 0;

    ~Dummy()
    {
        Console.WriteLine("~Dummy --> " + i);
    }
}

注意finalizer,它可以让我们看到 GC 收集它的确切时间。

现在,考虑这个辅助函数:它创建一个新的Dummy d,然后将它包装在一个函数闭包中并返回该闭包。

static Func<int> MakeFunc()
{
    Dummy d = new Dummy();
    Func<int> f = () => {
        d.i++;
        Console.WriteLine("Func invoked, d.i is now " + d.i);
        return d.i;
    };
    return f;
}

我们可以这样称呼它:

public static void Main()
{
    Console.WriteLine("1");

    Func<int> f = MakeFunc();
    f();

    Console.WriteLine("2");

输出:

1
Func invoked, d.i is now 1
2

我们可以强制一个垃圾回收周期:

    GC.Collect();
    GC.WaitForPendingFinalizers();

    Console.WriteLine("3");

输出:

3

注意我们仍然没有看到 finalizer 运行! Dummy 对象还活着,被函数闭包持有。

其实我们可以继续调用函数:

    f();

    Console.WriteLine("4");

输出:

Func invoked, d.i is now 2
4

当我们删除引用并强制另一个垃圾回收周期时,Dummy 实例将被最终确定:

    f = null;

    GC.Collect();
    GC.WaitForPendingFinalizers();

    Console.WriteLine("5");

输出:

~Dummy --> 2
5

P.S.:这里我使用GC.Collect() 进行演示。通常没有必要(也不建议)在生产代码中这样调用它。

【讨论】:

    【解决方案2】:

    如果仍然有对它的引用,GC 将不会删除该实例。包含对字段的引用的 Func 将持有对对象的引用(闭包)。

    【讨论】:

    • 感谢您的澄清! :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-23
    • 1970-01-01
    相关资源
    最近更新 更多