2010-11-13 12 views
27

Ich habe drei Fragen zu Veranstaltungen:Sollte ich mich von Veranstaltungen abmelden?

  1. Sollte ich immer abmelden Ereignisse, die gezeichnet wurden?
  2. Was passiert, wenn ich nicht mache?
  3. In den folgenden Beispielen, wie würden Sie die abonnierten Ereignisse abbestellen?

Ich zum Beispiel habe diesen Code:

Ctor: Zweck: Für Datenbankeigenschaft Updates

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

und diese: Zweck: Für GUI-Bindung des Modells in Viewmodels wickeln

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); 
} 
+0

http: // Stapelüberlauf.com/questions/1061727/Ist-es-schlecht-zu-nicht-Registrierung-Event-Handler – SwDevMan81

Antwort

23

1) Es kommt darauf an. Normalerweise ist es eine gute Idee, aber es gibt typische Fälle, in denen Sie nicht müssen. Wenn Sie sicher sind, dass das abonnierende Objekt die Ereignisquelle überlebt, sollten Sie sich grundsätzlich abmelden, da sonst eine unnötige Referenz entsteht.

Wenn jedoch das Objekt zu eigenen Veranstaltungen abonniert, wie im folgenden Beispiel:

<Window Loaded="self_Loaded" ...>...</Window> 

--then Sie nicht zu tun haben.

2) Das Abonnieren eines Ereignisses macht einen zusätzlichen Verweis auf das Subskriptionsobjekt. Wenn Sie sich also nicht abmelden, wird Ihr Objekt möglicherweise durch diese Referenz am Leben erhalten, wodurch effektiv ein Speicherleck entsteht. Durch das Abmelden entfernen Sie diese Referenz. Beachten Sie, dass das Problem bei der Selbstabonnement nicht auftritt.

3) Sie können wie das tun:

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

wo

void PropertyChangedHandler(object o, PropertyChangedEventArgs e) 
{ 
    switch (e.PropertyName) 
    { 
     case "FirstName": break; 
     case "LastName": break; 
    } 
} 
+0

wenn ich Ihren unteren Code kann ich nicht kompilieren? Ich habe ein ";" hinter dem letzten "}", aber es kompiliert nicht? UND wo sollte ich den vorgeschlagenen Code abbestellen? – Elisabeth

+0

@Lisa: (1) Was ist die Fehlermeldung, die Sie bekommen? (2) Sie sollten sich abmelden, wenn Sie die Ereignisse nicht mehr benötigen. Oft ist dies am Ende der Lebensdauer Ihres Objekts. Wenn Sie mehr über das Objekt erfahren könnten, das die Ereignisse abonnieren wird, wäre es einfacher zu verstehen, wann Sie sich abmelden müssen. – Vlad

+0

Überprüfen Sie meinen obigen Code Ich abonniere 3 Ereignisse OnXXX-Methoden. Ein PeriodViewModel kann weder ein DocumentListViewModel löschen. – Elisabeth

4

Sie können sich this article auf MSDN ansehen. Zitat:

Um Ihre Event-Handler von wird aufgerufen zu verhindern, wenn das Ereignis angehoben ist, austragen einfach aus dem Ereignisse. Um Ressourcen Lecks zu verhindern, ist es wichtig, von Ereignissen abzubestellen, bevor Sie ein Abonnentenobjekt entsorgen. Bis Sie von einem Ereignis abmelden , die Multicastdelegat, die das Ereignis im Verlagsobjekt zugrunde hat einen Bezug auf die Delegierten, dass der Veranstaltung Handler des Teilnehmers kapselt. Solange das Veröffentlichungsobjekt diese Referenz enthält, wird Ihr Abonnentenobjekt nicht als Müll erfasst.

1

1.) Sollte ich immer desubscribe Ereignisse, die gezeichnet wurden?
Normalerweise ja.Die einzige Ausnahme ist, wenn das Objekt, auf dem Sie abonniert haben, nicht mehr referenziert wird und in Kürze eine Müllsammlung stattfindet.

2.) Was passiert, wenn ich NICHT mache?
Das Objekt, auf dem Sie abonniert haben, enthält einen Verweis auf den Delegaten, der wiederum einen Verweis auf seinen Zeiger this enthält, und Sie erhalten dadurch ein Speicherleck.
Oder, wenn der Handler ein lamda war, wird er auf allen lokalen Variablen festhalten, die er gebunden hat, die also auch nicht gesammelt werden.

+1

Ich möchte darauf hinweisen, dass Ihr Handler nach der Verwendung des Objekts aufgerufen werden kann, wenn Sie das Ereignis nicht abbestellen. wenn das Objekt wegwerfbar ist, nachdem das Objekt entsorgt wurde. Dies kann sich auf den Handler-Code auswirken, wenn er nicht verwaltete Ressourcen verwendet, die von der Dispose freigegeben wurden. –

39

Nun, nehmen wir die letzte Frage zuerst. Sie können nicht zuverlässig von einem Ereignis abmelden, das Sie direkt mit einem Lambda-Ausdruck abonniert haben. Sie entweder müssen eine Variable um mit dem Delegaten in halten (so können Sie immer noch einen Lambda-Ausdruck verwenden) oder müssen Sie stattdessen eine Methodengruppe Konvertierung verwenden.

Jetzt, ob Sie tatsächlich abbestellen müssen, hängt es von der Beziehung zwischen dem Ereignisproduzenten und dem Ereigniskonsumenten ab. Wenn der Ereignisproduzent länger als der Ereigniskonsument leben soll, sollten Sie abbestellen - denn sonst erhält der Produzent einen Hinweis auf den Verbraucher und hält ihn länger am Leben, als er sein sollte. Der Event-Handler wird auch solange aufgerufen, wie der Producer ihn erzeugt.

Jetzt in vielen Fällen ist das kein Problem - zum Beispiel in einer Form die Schaltfläche, die das Ereignis Click löst wahrscheinlich für etwa so lange wie das Formular, auf dem es erstellt wird, wo der Handler in der Regel abonniert ist .. Daher müssen Sie sich nicht abmelden. Dies ist sehr typisch für eine GUI.

Wenn Sie eine WebClient nur für den Zweck einer einzigen asynchronen Anfrage erstellen, abonnieren Sie das entsprechende Ereignis und starten Sie die asynchrone Anfrage, dann ist die WebClient selbst für die Garbage Collection berechtigt, wenn die Anfrage beendet ist (vorausgesetzt, Sie nicht Behalte einen Verweis woanders).

Grundsätzlich sollten Sie immer die Beziehung zwischen dem Hersteller und dem Verbraucher berücksichtigen. Wenn der Produzent länger leben wird als der Verbraucher, oder, wird es das Ereignis weiter erhöhen, nachdem Sie nicht mehr daran interessiert sind, dann sollten Sie sich abmelden.

+0

Ereignis Produzent = DocumentListViewModel? Event Consumer = eine OnXXX-Methode von oben? Sie meinen, wenn ich ein documentListViewModel löschen werde, gibt es immer noch einen Verweis auf eine OnXXX-Methode? Wo sollte ich mich von Veranstaltungen in einem ViewModel abmelden? – Elisabeth

2

Sie müssen sich nicht von einem Ereignis abmelden, wenn die zu subskribierende Instanz denselben Umfang hat wie die abonnierte Instanz.

Nehmen wir an, Sie sind ein Formular und Sie abonnieren ein Steuerelement, bilden diese beiden zusammen eine Gruppe. Wenn Sie jedoch eine zentrale Klasse haben, die Formulare verwaltet, und Sie haben das Ereignis Closed dieses Formulars abonniert, bilden diese keine Gruppe zusammen und Sie müssen abbestellen, sobald das Formular geschlossen ist.

Wenn ein Ereignis abonniert wird, erstellt die abonnierte Instanz einen Verweis auf die abonnierte Instanz. Dies verhindert eine Speicherbereinigung. Wenn Sie also eine zentrale Klasse haben, die Formularinstanzen verwaltet, bleiben alle Formulare im Speicher.

WPF ist eine Ausnahme, da es ein schwaches Ereignismodell aufweist, bei dem Ereignisse mit schwachen Referenzen abonniert werden und das Formular nicht im Speicher gehalten wird. Es ist jedoch immer noch die beste Vorgehensweise, sich abzumelden, wenn Sie nicht Teil des Formulars sind.

Verwandte Themen