Czy powinienem zrezygnować z subskrypcji wydarzeń? [duplikat]

To pytanie ma już odpowiedź tutaj:

Mam 3 pytania dotyczące wydarzeń:

  1. Czy zawsze powinienem zrezygnować z subskrypcji wydarzeń, które zostały subskrybowane?
  2. Co się stanie, jeśli tego nie zrobię?
  3. w poniższych przykładach, w jaki sposób można zrezygnować z subskrypcji subskrybowanego wydarzenia?

Mam na przykład ten kod:

Ctor: Purpose: for database property updates

this.PropertyChanged += (o, e) =>
{
    switch (e.PropertyName)
    {
        case "FirstName": break;
        case "LastName": break;
    }
};

And this: Purpose: for GUI-binding wrap the model into viewmodels

ObservableCollection<Period> periods = _lpRepo.GetDailyLessonPlanner(data.DailyDate);
PeriodListViewModel = new ObservableCollection<PeriodViewModel>();

foreach (Period period in periods)
{
    PeriodViewModel periodViewModel = new PeriodViewModel(period,_lpRepo);
    foreach (DocumentListViewModel documentListViewModel in periodViewModel.DocumentViewModelList)
    {
        documentListViewModel.DeleteDocumentDelegate += new Action<List<Document>>(OnDeleteDocument);
        documentListViewModel.AddDocumentDelegate += new Action(OnAddDocument);
        documentListViewModel.OpenDocumentDelegate += new Action<int, string>(OnOpenDocument);
    }
    PeriodListViewModel.Add(periodViewModel);
}
Author: abatishchev, 2010-11-13

5 answers

1) to zależy. Zazwyczaj jest to dobry pomysł, ale są typowe przypadki, w których nie trzeba. Zasadniczo, jeśli jesteś pewien, że subskrybujący obiekt przeżyje Źródło zdarzenia, powinieneś zrezygnować z subskrypcji, w przeciwnym razie stworzyłoby to niepotrzebne odniesienie.

Jeśli jednak Twój obiekt subskrybuje własne zdarzenia, jak w poniższym:

<Window Loaded="self_Loaded" ...>...</Window>
Więc nie musisz.

2) zapisanie się na wydarzenie stanowi dodatkowe odniesienie do zapisania się obiekt. Jeśli więc nie zrezygnujesz z subskrypcji, Twój obiekt może zostać utrzymany przy życiu przez to odniesienie, co skutecznie spowoduje wyciek pamięci. Rezygnując z subskrypcji, usuwasz to odniesienie. Pamiętaj, że w przypadku subskrypcji własnej problem nie pojawia się.

3) możesz zrobić tak:

this.PropertyChanged += PropertyChangedHandler;
...
this.PropertyChanged -= PropertyChangedHandler;

Gdzie

void PropertyChangedHandler(object o, PropertyChangedEventArgs e)
{
    switch (e.PropertyName)
    {
        case "FirstName": break;
        case "LastName": break;
    }
}
 25
Author: Vlad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-13 14:00:47

Zacznijmy od ostatniego pytania. Nie możesz nie możesz niezawodnie zrezygnować ze zdarzenia, które subskrybowałeś bezpośrednio za pomocą wyrażenia lambda. Musisz albo zachować zmienną z delegatem w (więc nadal możesz używać wyrażenia lambda) lub zamiast tego musisz użyć konwersji grupy metod.

Teraz, jeśli chodzi o to, czy rzeczywiście musisz zrezygnować z subskrypcji, zależy to od relacji między producentem wydarzenia a konsumentem wydarzenia. Jeśli producent zdarzenia powinien żyć dłużej niż konsument zdarzenia, ty powinieneś zrezygnować z subskrypcji - ponieważ w przeciwnym razie producent będzie miał odniesienie do konsumenta, utrzymując go przy życiu dłużej niż powinien być. Opiekun wydarzenia będzie również otrzymywać wezwanie tak długo, jak producent produkuje go.

Teraz w wielu przypadkach nie jest to problemem - na przykład w formularzu przycisk, który podnosi Zdarzenie Click, może żyć tak długo, jak FORMULARZ, na którym zostało utworzone, gdzie program obsługi jest zazwyczaj subskrybowany... nie ma więc potrzeby rezygnacji z subskrypcji. Jest to bardzo typowe dla GUI.

Podobnie jeśli utworzysz WebClient wyłącznie na potrzeby pojedynczego żądania asynchronicznego, subskrybujesz odpowiednie zdarzenie i uruchomisz żądanie asynchroniczne, to samo WebClient będzie kwalifikowało się do usunięcia śmieci po zakończeniu żądania (zakładając, że nie przechowujesz referencji w innym miejscu).

Zasadniczo, zawsze należy wziąć pod uwagę związek między producent i konsument. Jeśli producent będzie żył dłużej niż chcesz, aby konsument, lub będzie nadal podnosił wydarzenie, gdy nie będziesz już nim zainteresowany, powinieneś zrezygnować z subskrypcji.

 43
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-13 13:54:07

Możesz spojrzeć na Ten artykuł na MSDN. Cytat:

Aby zapobiec obsłudze zdarzenia wywoływane, gdy zdarzenie jest podniesiony, wystarczy wypisać się z wydarzenie. W celu zapobiegania zasobom przecieki, ważne jest, aby zrezygnować z subskrypcji z wydarzeń przed pozbyciem się obiekt abonencki. Until you wypisanie się z wydarzenia, delegat multicast, który leży u podstaw wydarzenie w obiekcie wydawniczym ma odniesienie do delegata, który enkapsuje Zdarzenie Abonenta handler. Dopóki Wydawnictwo obiekt posiada to odniesienie, Twoje obiekt subskrybenta nie będzie śmieciem zebrane.

 4
Author: Darin Dimitrov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-13 13:51:19

Nie musisz rezygnować ze zdarzenia, gdy wystąpienie subskrybowane ma taki sam zakres jak wystąpienie subskrybowane.

Powiedzmy, że jesteś formularzem i subskrybujesz kontrolę, te dwa razem tworzą grupę. Jeśli jednak masz klasę centralną, która zarządza formularzami i subskrybujesz Zdarzenie Closed tego formularza, nie tworzą one razem grupy i musisz zrezygnować z subskrypcji po zamknięciu formularza.

Subscribing to an zdarzenie powoduje, że subskrybowana instancja tworzy odniesienie do subskrybowanej instancji. Zapobiega to zbieraniu śmieci. Tak więc, gdy masz klasę centralną, która zarządza instancjami formularza, zachowasz wszystkie formularze w pamięci.

WPF jest wyjątkiem, ponieważ ma Model słabych zdarzeń, w którym zdarzenia są subskrybowane za pomocą słabych odniesień i nie przechowuje formularza w pamięci. Jednak nadal najlepszą praktyką jest anulowanie subskrypcji, gdy nie jesteś częścią formularza.

 4
Author: Pieter van Ginkel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-13 13:55:39

1.) Czy zawsze powinienem anulować subskrypcję wydarzeń, które zostały subskrybowane?
Zazwyczaj tak. Jedynym wyjątkiem jest sytuacja, gdy obiekt, na którym subskrybujesz, nie jest już odwoływany i wkrótce zostanie zbierany jako śmieci.

2.) Co się stanie, jeśli tego nie zrobię?
Obiekt, na którym subskrybujesz, będzie zawierał odniesienie do delegata, który z kolei zawiera odniesienie do jego wskaźnika this, a tym samym otrzymasz wyciek pamięci.
Albo jeśli opiekun był lamda to będzie trzymał się niezależnie od zmiennych lokalnych, które wiązał, które w ten sposób również nie będą zbierane.

 1
Author: CodesInChaos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-13 13:53:58