Доступ к полю вне класса экземпляра - PullRequest
0 голосов
/ 21 ноября 2018

Давайте рассмотрим этот класс:

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;
}

Мой вопрос заключается в том, что действительно происходит, потому что я изначально предполагал, что экземпляр будет удален GC, когда SomeFunctions вернется, что будет означать, что _flag прекратитсясуществовать, и функция будет пытаться получить доступ к несуществующему полю, когда в конечном итоге вызывается откуда-то, но это не то, что происходит.Код, кажется, работает.Поле как-то сохраняется в закрытии?

Спасибо за разъяснения.

Ответы [ 2 ]

0 голосов
/ 21 ноября 2018

В интересах простоты я буду использовать Dummy класс (вместо вашего общего A):

class Dummy
{
    public int i = 0;

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

Обратите внимание на финализатор , который позволяет нампосмотрите точное время, в которое он будет собран 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

Обратите внимание, что мы еще не видели, как работает финализатор !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

PS: Здесь я использую GC.Collect() в демонстрационных целях.Как правило, нет необходимости (и нецелесообразно) называть это так в рабочем коде.

0 голосов
/ 21 ноября 2018

ГХ не удалит экземпляр, если на него все еще есть ссылки.А Func <>, содержащий ссылку на поле, будет содержать ссылку на объект (замыкание).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...