简体   繁体   English

访问范围外的类实例的字段

[英]Accessing field of out of scope class instance

Let's consider this class: 让我们考虑这个类:

public class A<T>
{
    private bool _flag;

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

Now, let's imagine that the Function property somehow accesses the _flag field with both read and write in its body. 现在,让我们想象一下Function属性以某种方式访问​​_flag字段并对其主体进行读取和写入。 Then if I use the class A like this: 然后,如果我像这样使用A类:

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

My question is what really happens, because I have originally assumed the instance would be disposed by GC when the SomeFunctions returns, which would mean the _flag would cease to exist and the Function would try to access nonexistent field when eventually called from somewhere, but that isn't what happens. 我的问题是真正发生的情况,因为我最初假设该实例将在SomeFunctions返回时由GC处理,这意味着_flag将不再存在,并且当最终从某个地方调用该函数时,该函数将尝试访问不存在的字段,但是那不会发生什么。 The code seems to work. 该代码似乎有效。 Is the field somehow preserved in a closure? 字段是否以某种方式保留在封闭中?

Thanks for clarification. 感谢您的澄清。

The GC will not remove the instance if there are still references to it. 如果仍有实例引用,GC将不会删除该实例。 And a Func<> containing a reference to a field will hold a reference to the object (closure). 包含对字段的引用的Func <>将保存对对象的引用(关闭)。

In the interest of simplicity, I will use a Dummy class (instead of your generic A ): 为了简单起见,我将使用Dummy类(而不是您的通用A ):

class Dummy
{
    public int i = 0;

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

Note the finalizer , which allows us to see the exact time at which it will be collected by the GC. 注意finalizer ,它使我们能够看到GC收集它的确切时间。

Now, consider this helper function: it creates a new Dummy d , then wraps it in a function closure and returns that closure. 现在,考虑这个辅助函数:它创建一个新的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;
}

We can call it like this: 我们可以这样称呼它:

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

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

    Console.WriteLine("2");

Output: 输出:

1
Func invoked, d.i is now 1
2

We can force a garbage collection cycle: 我们可以强制垃圾回收周期:

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

    Console.WriteLine("3");

Output: 输出:

3

Note how we still haven't seen the finalizer running! 注意我们还没有看到终结器运行! The Dummy object is still alive, being held by the function closure. Dummy对象仍处于活动状态,由函数闭包保留。

In fact, we can continue to call the function: 实际上,我们可以继续调用该函数:

    f();

    Console.WriteLine("4");

Output: 输出:

Func invoked, d.i is now 2
4

That Dummy instance will be finalized when we drop the reference and force another garbage collection cycle: 当我们删除引用并强制执行另一个垃圾回收周期时,该Dummy实例将最终确定:

    f = null;

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

    Console.WriteLine("5");

Output: 输出:

~Dummy --> 2
5

PS: Here I am using GC.Collect() for demonstration purposes. PS:在这里,我使用GC.Collect()进行演示。 It's generally unnecessary (and inadvisable) to call it like this in production code. 通常不需要在生产代码中这样称呼它(也不可取)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM