Ich habe das Essential C # 3.0-Buch gelesen und frage mich, ob das eine gute Möglichkeit ist, Delegierte auf Null zu prüfen?:
%Vor%Ändert sich die Lösung, wenn der Typ unveränderlich ist? Ich dachte, vielleicht mit Unveränderlichkeit würden Sie nicht in dieses Threading-Problem stoßen.
Es wurde viel darüber diskutiert.
Kurz gesagt: Sie können nicht garantieren, dass der Handler auch dann gültig ist, wenn Sie diesen Kopier- / Prüf-Null- / Ausführungsschritt ausführen.
Das Problem ist, wenn OnTemperatureChange zwischen dem Zeitpunkt, an dem Sie es kopieren, und der Zeit, zu der Sie die Kopie ausführen, nicht registriert ist, ist es wahrscheinlich wahr, dass Sie den Listener trotzdem nicht ausführen wollen.
Sie können genauso gut tun:
%Vor%Und behandeln Sie eine Null-Referenz-Ausnahme.
Ich füge manchmal einen Standard-Handler hinzu, der nichts tut, nur um die Null-Referenz-Ausnahme zu verhindern, aber das macht die Leistung ziemlich ernst, besonders in dem Fall, wo kein anderer Handler registriert ist.
Ich verweise auf Eric Lippert .
Meine ursprüngliche Antwort bezog sich auf die Verwendung von Standard-Handlern, aber ich empfahl nicht, eine temporäre Variable zu verwenden, was ich nun auch als gute Praxis für den Artikel akzeptiere.
Es gibt einen Grund für den Code, den Sie haben gegeben ist über C. Ross-Version empfohlen. Allerdings hat John auch Recht, dass es noch ein weiteres Problem gibt, wenn ein Ereignis in der Zwischenzeit nicht registriert wird. Der Blog, den ich verlinkt habe, empfiehlt, dass der Handler sicherstellt, dass sie auch nach Nicht-Registrierung aufgerufen werden können.
Erstens veröffentlichen Sie eigentlich kein Event - im Moment ist Ihr Code also "gefährdet", wenn die Leute ihn komplett durcheinander bringen. Es sollte sein:
%Vor% Der Name "CurrentTemperatureChanged" ist wichtig für die Datenbindung (es gibt eine Konvention, die die Laufzeit verwendet - bei einer Eigenschaft Foo wird nach FooChanged gesucht). Allerdings , IMO, das sollte nur normal EventHandler
sein. Die Datenbindung sucht nach EventHandler
, aber was noch wichtiger ist: Sie geben keine Informationen weiter, wenn der Abonnent nicht schon mit obj.CurrentTemperature
fertig wird.
Ich gebe den Rest der Antwort in TemperatureChangeHandler
, aber ich würde Sie (wieder) ermutigen, zu EventHandler
:
Der Ansatz:
%Vor%ist vernünftig, aber (laut anderen Antworten) besteht ein geringes Risiko für Anrufer, die denken, dass sie das Ereignis nicht getrennt haben. In Wirklichkeit unwahrscheinlich.
Ein anderer Ansatz ist eine Erweiterungsmethode:
%Vor%Dann in Ihrer Klasse können Sie einfach verwenden:
%Vor%Wenn die Thermostat-Klasse nicht threadsicher sein muss, dann ist der obige Code in Ordnung - solange nur ein Thread auf diese Instanz von Thermostat zugreift, gibt es keine Möglichkeit, dass sich OnTemperatureChange zwischen dem Test auf Null aufheben lässt und der Aufruf an das Ereignis.
Wenn Sie Thermostat-Thread sicher machen müssen, dann sollten Sie vielleicht einen Blick auf den folgenden Artikel werfen (neu für mich, sieht wie eine gute Lektüre aus):
Zur Erinnerung: Die Empfehlung lautet, dass Sie Ihre Klassen so entwickeln, dass sie nicht threadsicher sind, es sei denn, Threadsicherheit wird explizit benötigt, da dies die Komplexität Ihres Codes erheblich erhöhen kann.
Ich sehe nur ein bisschen Refactoring, das gemacht werden könnte, aber ansonsten sieht es gut aus ...
%Vor%Verwenden Sie ein Fragezeichen für einen bedingten Zugriff:
OnTemperatureChange?.Invoke();