2009-01-12 8 views
8

Ich bin auf das gestoßen, was ein allgemeines Problem sein muss. Wenn ich ein Ereignis habe, das von mehreren verschiedenen Klassen abonniert werden kann, führt eine Ausnahme, die von einer dieser Klassen ausgelöst wird, zum Abbruch der Rückrufkette. Da ich a priori nicht weiß, in welcher Reihenfolge der Callback ausgeführt wird, kann dies zu unvorhersehbaren Statusänderungen für einige Klassen und nicht für andere führen.Wie stoppe ich Ausnahmen, die meine Delegiertenkette zerstören?

In der Bibel (CLR via C#, verwende ich C# 2.0) gibt es einen kurzen Absatz über MulticastDelegate.GetInvocationList, um dies zu umgehen, aber nichts mehr. Also meine Frage ist: Was ist der beste Weg, damit umzugehen? Muss ich jedes Mal, wenn ich eine Veranstaltung habe, MulticastDelegate.GetInvocationList benutzen? Oder muss ich alle Methoden einschließen, die als Teil der Delegiertenkette in einer Art Rollback-Mechanismus aufgerufen werden können? Warum sind all diese Optionen im Vergleich zum einfachen Event/Delegate-Modell, das in C# so einfach zu verwenden ist, so kompliziert? Und wie kann ich den einfachen Weg benutzen, ohne mit einem beschädigten Zustand zu enden?

Danke!

+0

note antworten Sie auf Ihren Kommentar –

Antwort

11

Wenn Sie einfach einen Delegaten aufrufen, ruft er alle Zielmethoden der Reihe nach auf. Sie müssen GetInvocationList verwenden, wenn man sie einzeln ausgeführt werden soll - zum Beispiel:

  • Cancel nach jedem
  • überprüfen Sie den Rückgabewert jedes
  • erfassen nach dem Ausfall eines einzelnen Ziel weiterhin

Wie für den besten Weg, es zu verwenden: Wie soll es sich verhalten? Es ist mir nicht klar, ... zum Beispiel, könnte dies durchaus auch eine Erweiterungsmethode passen:

static void InvokeIgnoreErrors(this EventHandler handler, 
     object sender) { 
    if(handler != null) { 
     foreach(EventHandler subHandler in handler.GetInvocationList()) { 
      subHandler(sender, EventArgs.Empty); 
     } 
    } 
} 

Dann können Sie einfach myHandler.InvokeIgnoreErrors(this); (zum Beispiel) nennen. sein

könnte Ein weiteres Beispiel:

static bool InvokeCheckCancel(this CancelEventHandler handler, 
     object sender) { 
    if(handler != null) { 
     CancelEventArgs args = new CancelEventArgs(false); 
     foreach(CancelEventHandler subHandler in handler.GetInvocationList()) { 
      subHandler(sender, args); 
      if(args.Cancel) return true; 
     } 
    } 
    return false; 
} 

die nach dem ersten Ereignis stoppt fordert Löschung.

+0

OK, das scheint eine nette Verwendung für Erweiterungsmethoden. Leider stecke ich in C# 2.0 fest - irgendwelche Ideen, wie ich es hier elegant machen könnte? Vielen Dank! –

+1

Verwenden Sie dann einfach eine statische Methode. Entfernen Sie das "This" und verwenden Sie einfach YourUtilityClass.InvokeIgnoreErrors (myHandler, this); –

1

Anstatt zu ändern, wie Sie Ereignisse aufrufen, sollten Sie Ihre Event-Handler überprüfen. Meiner Meinung nach sollten Event-Handler-Methoden immer so geschrieben werden, dass sie "sicher" sind und niemals Ausnahmen propagieren lassen. Dies ist besonders wichtig, wenn Sie Ereignisse in GUI-Code behandeln, bei denen das Ereignis durch externen Code aufgerufen wird, aber zu jeder Zeit eine gute Gewohnheit ist.

+0

Was meinst du mit "propagieren"? Ich brauche eindeutig Methode # 3 in meiner Delegiertenkette, um mich zu informieren, wenn es abgestürzt ist, da ich dann eventuell vorgenommene Änderungen zurücksetzen muss, die in Methoden # 1 und # 2 durchgeführt wurden (aber nicht Methode # 4). –

+0

Dann missbrauchen Sie das Beobachtermuster, der Punkt der Verwendung von Ereignissen ist ein lose gekoppeltes Benachrichtigungssystem. Hier müssen nicht nur die Abonnenten und der Herausgeber die Implementierungsdetails voneinander kennen, die Abonnenten müssen auch Implementierungsdetails über andere Abonnenten kennen. –

+0

Das besagt, dass es definitiv Fälle gibt, in denen Sie die Aufrufliste verwenden möchten, anstatt den Delegierten direkt aufzurufen, wie Marc es beschrieben hat. –

Verwandte Themen