Должны ли настраиваемые события иметь значение null при удалении объекта? - PullRequest
14 голосов
/ 06 января 2010

Допустим, у нас есть 2 объекта: вещатель и слушатель. Вещатель имеет событие Broadcast, на которое подписан Listener. Если прослушиватель удаляется без отмены подписки на событие Broadcast, он будет сохранен в памяти из-за того, что делегат события ссылается на него, который содержит Broadcaster.

Что меня интересует, так это то, что Broadcaster удаляется без отписки слушателя или настройки Broadcaster Broadcast = null Будет ли Broadcaster сохраняться в памяти?

Мне не удалось найти ничего с точным ответом на этот вопрос, кроме одного блоггера, который считает, что если для события не задано значение null, источник останется в памяти (найдено здесь ).

Я бы хотел услышать объяснение, почему или почему нет.

Спасибо.

UPDATE: Тема форума, где один разработчик указывает, что события должны быть установлены на нуль, но Джон Скит указывает, что это не обязательно, но не уточняет.

Ответы [ 2 ]

9 голосов
/ 06 января 2010

Обратите внимание, что делегаты не поддерживают работу издателя (они поддерживают только цель = подписчика), поэтому никакое количество подписок не может (само по себе) поддерживать работу вещателя. Таким образом, с этой точки зрения не имеет значения, будет ли он утилизирован или нет. Когда нет объектов, ссылающихся на вещателя (и подписки на события не имеют значения для этого), он будет иметь право на сбор.

По существу , делегатом является (список) пара (ов) ссылок MethodInfo и object; метод для вызова и объект для вызова как "arg0" (он же this). У него просто нет ссылки на объект , вызывающий событие.

Вот свидетельство того, что слушатель не поддерживает источник живым; Вы должны увидеть, что «Источник 1» собирается, хотя у нас все еще есть соответствующий прослушиватель, который подписан. Как и ожидалось, «Слушатель 2» не получает , а не , поскольку у нас все еще есть соответствующий вещатель:

class DataSource
{
    public DataSource(string name) { this.name = name; }
    private readonly string name;
    ~DataSource() { Console.WriteLine("Collected: " + name); }

    public event EventHandler SomeEvent;
}
class DataListener
{
    public DataListener(string name) { this.name = name; }
    private readonly string name;
    ~DataListener() { Console.WriteLine("Collected: " + name); }
    public void Subscribe(DataSource source)
    {
        source.SomeEvent += SomeMethodOnThisObject;
    }
    private void SomeMethodOnThisObject(object sender, EventArgs args) { }
}

static class Program
{
    static void Main()
    {
        DataSource source1 = new DataSource("Source 1"),
                source2 = new DataSource("Source 2");
        DataListener listener1 = new DataListener("Listener 1"),
                listener2 = new DataListener("Listener 2");
        listener1.Subscribe(source1);
        listener2.Subscribe(source2);
        // now we'll release one source and one listener, and force a collect
        source1 = null;
        listener2 = null;
        GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
        GC.WaitForPendingFinalizers(); // source 1 gets collected, ONLY

        Console.WriteLine("Done");
        Console.ReadLine();
        GC.KeepAlive(source2); // prevents collection due to optimisation
        GC.KeepAlive(listener1); // prevents collection due to optimisation
    }
}
4 голосов
/ 06 января 2010

Нет. Цель делегата в событии Broadcast ссылается на объект Listener. Это сохранит объект Слушателя живым. Объект Listener не имеет обратной ссылки на объект Broadcast.

Остерегайтесь терминологии. Удаление объекта Broadcast ничего не делает. Это должен быть сборщик мусора, который может произойти только тогда, когда не осталось ссылок на объект. Когда это происходит, объект делегата также будет автоматически собран, поскольку единственная ссылка на него - внутренний список целей делегата, поддерживаемый объектом делегата частного события. Это также удаляет ссылку, которую делегат имеет на слушателя. Если нет никаких других ссылок на слушателя, он будет также собран. Если это все еще так, он просто больше не будет получать уведомления о событиях. Короче говоря: вам не нужно явно устанавливать событие равным нулю в классе Broadcast.

Не совсем то же самое в слушателе, на него ссылается событие, на которое он подписан. Если он объявлен непригодным для бизнеса (удален), но вещатель все еще активен, он должен явно удалить свою подписку на события. Класс SystemEvents является экстремальной версией этого, его события являются статическими. Вы, как правило, замечаете срабатывание событий на делегате, который ссылается на удаленного слушателя.

Большинство практических объектных моделей пытаются гарантировать, что объекты слушателя исчезают, когда родитель уходит. Windows Forms был бы хорошим примером. Тогда нет необходимости явно отписываться от событий.

...