Нужно ли отписываться от событий

Тема в разделе ".NET", создана пользователем ][tiger, 21 мар 2006.

Статус темы:
Закрыта.
  1. ][tiger

    ][tiger Гость

    К примеру у нас есть некая типизированная коллекция. У элементов этой коллекции есть какие-то собития, которые эта самая коллекция должна отслеживать (то есть при добавлении элемента происходит подписка).

    Нужно ли специально отписываться от таких событий в случае удаления из коллекции одного из элементов (учитывая, что больше никто не содержит ссылок на этот элемент)?

    Соберется ли этот объект сборщиком мусора или будет считаться, что коллекция все еще содержит на него ссылку?
     
  2. ][tiger

    ][tiger Гость

    Ну и сам уже отвечу :) Если ссылок больше нет, то GC соберет этот объект и без отписки от событий. Ведь на самом деле, это сам объект ссылается на делегат.... Однако, для коллекции все-таки нужно отписаться...
     
  3. admin

    admin Well-Known Member

    Регистрация:
    8 авг 2003
    Сообщения:
    2.811
    Симпатии:
    0
    Для: ][tiger
    интересный подход :)
     
  4. Dr.Gigabit

    Dr.Gigabit Гость

    <!--QuoteBegin-][tiger+21:03:2006, 17:35 -->
    <span class="vbquote">(][tiger @ 21:03:2006, 17:35 )</span><!--QuoteEBegin-->GC соберет этот объект и без отписки от событий
    [snapback]32240" rel="nofollow" target="_blank[/snapback]​
    [/quote]


    <!--QuoteBegin-][tiger+21:03:2006, 17:35 -->
    <span class="vbquote">(][tiger @ 21:03:2006, 17:35 )</span><!--QuoteEBegin-->для коллекции все-таки нужно отписаться
    [snapback]32240" rel="nofollow" target="_blank[/snapback]​
    [/quote]


    Не уловил мысли
     
  5. ][tiger

    ][tiger Гость

    Мысль такова, что если мы подписаны на событие какого-то объекта, но ссылок больше на него нет, то GC соберет его как мусор.

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

    Сам себе ответил на вопрос потому, что догадался запустить SnippetCompiler набросать пример и проверить - будет ли объект собираться GC, если ссылок нет, но есть подписчики. Ответ - будет.
     
  6. karlito

    karlito Гость

    Не уверен. За первый проход нет, за второй - да.

    Не ответил на самый главный вопрос - ПОЧЕМУ?
     
  7. ][tiger

    ][tiger Гость

    <!--QuoteBegin-karlito+23:03:2006, 17:05 -->
    <span class="vbquote">(karlito @ 23:03:2006, 17:05 )</span><!--QuoteEBegin-->Не уверен. За первый проход нет, за второй - да.
    [snapback]32375" rel="nofollow" target="_blank[/snapback]​
    [/quote]

    Я же говорю, что проверил. Собирает. Достаточно объявить деструктор и посмотреть, вызывается ли при вызове GC.Collect(). Так вот - вызывается.


    <!--QuoteBegin-karlito+23:03:2006, 17:05 -->
    <span class="vbquote">(karlito @ 23:03:2006, 17:05 )</span><!--QuoteEBegin-->Не ответил на самый главный вопрос - ПОЧЕМУ?
    [snapback]32375" rel="nofollow" target="_blank[/snapback]​
    [/quote]

    Опять-таки, ответил. Потому что не подписчик ссылается физически на объект с событием, а объект который содержит событие ссылается на делегаты, которые подписаны на его событие. Я же говорил уже.
     
  8. Dr.Gigabit

    Dr.Gigabit Гость


    Ужас какой-то :) Примерчик можно в студию?
    Но сразу сомнения хотя бы в том, что частный случай не подтверждает правило. Имхо, не столь очевидное поведение.
     
  9. ][tiger

    ][tiger Гость

    Код (Text):
    using System;
    using System.Collections;

    public class MyClass
    {
    private static ObjectWithEvent _objWithEvent = new ObjectWithEvent();

    public static void Main()
    {
    _objWithEvent.Event += new EventHandler(ObjEventHandler);
    _objWithEvent = null;

    WL("Initiating garbage collection");
    GC.Collect();
    WL("GC.Collect finished");

    RL();
    }

    private static void ObjEventHandler(object sender, EventArgs e)
    {
    //do nothing
    }

    public static void WL(string text, params object[] args)
    {
    Console.WriteLine(text, args); 
    }

    private static void RL()
    {
    Console.ReadLine();
    }

    private static void Break()
    {
    System.Diagnostics.Debugger.Break();
    }
    }

    public class ObjectWithEvent
    {
    public event EventHandler Event;   

    ~ObjectWithEvent()
    {
    MyClass.WL("I've been collected");
    }
    }
    Результат:

    Initiating garbage collection
    I've been collected
    GC.Collect finished
     
  10. karlito

    karlito Гость

    Поправлю немного. После GC.Collect() необходимо вставить GC.WaitForPendingFinalizers(). Тогда результат во всех случаях будет как ты написал.

    Вот это я не понял.

    Понятно что можно и не отписываться от событий. Но это плохая практика. Допустим на этот объект ссылка имеется в другом месте, а ты вызываешь GC.Collect() и думаешь, что объект удалён из памяти и твой метод, который вызывается по событию, больше вызываться не будет. И в одно прекрасное утро он вызовется и ты этого не ждал, и приложение упало, и пользователи заволновались... :(
     
Загрузка...
Статус темы:
Закрыта.

Поделиться этой страницей