Mir ist völlig klar, dass das, was ich vorschlage, nicht den .NET-Richtlinien entspricht und daher wahrscheinlich allein aus diesem Grund eine schlechte Idee ist. Ich möchte dies jedoch aus zwei möglichen Perspektiven betrachten:
(1) Sollte ich in Betracht ziehen, dies für meine eigene Entwicklungsarbeit zu verwenden, die zu 100% für interne Zwecke bestimmt ist?
(2) Ist dies ein Konzept, das die Framework-Designer ändern oder aktualisieren könnten?
Ich denke darüber nach, eine Ereignissignatur zu verwenden, die einen stark typisierten "Absender" verwendet, anstatt ihn als "Objekt" zu tippen, was das aktuelle .NET-Entwurfsmuster ist. Das heißt, anstatt eine Standardereignissignatur zu verwenden, die folgendermaßen aussieht:
class Publisher
{
public event EventHandler<PublisherEventArgs> SomeEvent;
}
Ich erwäge die Verwendung einer Ereignissignatur, die einen stark typisierten Absenderparameter verwendet, wie folgt:
Definieren Sie zunächst einen "StrongTypedEventHandler":
[SerializableAttribute]
public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
Dies unterscheidet sich nicht allzu sehr von einer Aktion <TSender, TEventArgs>, aber durch die Verwendung von StrongTypedEventHandler
erzwingen wir, dass die TEventArgs von abgeleitet sind System.EventArgs
.
Als nächstes können wir als Beispiel den StrongTypedEventHandler in einer Publishing-Klasse wie folgt verwenden:
class Publisher
{
public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;
protected void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent(this, new PublisherEventArgs(...));
}
}
}
Die obige Anordnung würde es Abonnenten ermöglichen, einen stark typisierten Ereignishandler zu verwenden, für den kein Casting erforderlich ist:
class Subscriber
{
void SomeEventHandler(Publisher sender, PublisherEventArgs e)
{
if (sender.Name == "John Smith")
{
// ...
}
}
}
Mir ist völlig klar, dass dies mit dem Standardmuster für die Behandlung von .NET-Ereignissen bricht. Beachten Sie jedoch, dass ein Abonnent durch Kontravarianz auf Wunsch eine herkömmliche Signatur für die Ereignisbehandlung verwenden kann:
class Subscriber
{
void SomeEventHandler(object sender, PublisherEventArgs e)
{
if (((Publisher)sender).Name == "John Smith")
{
// ...
}
}
}
Das heißt, wenn ein Ereignishandler Ereignisse von unterschiedlichen (oder möglicherweise unbekannten) Objekttypen abonnieren muss, kann der Handler den Parameter 'sender' als 'object' eingeben, um die gesamte Breite potenzieller Absenderobjekte zu verarbeiten.
Abgesehen davon, dass ich gegen die Konvention verstoße (was ich nicht leicht nehme, glauben Sie mir), kann ich mir keine Nachteile vorstellen.
Hier können einige CLS-Konformitätsprobleme auftreten. Dies läuft in Visual Basic .NET 2008 zu 100% in Ordnung (ich habe es getestet), aber ich glaube, dass die älteren Versionen von Visual Basic .NET bis 2005 keine delegierte Kovarianz und Kontravarianz aufweisen. [Bearbeiten: Ich habe dies seitdem getestet und es wurde bestätigt: VB.NET 2005 und niedriger können dies nicht verarbeiten, aber VB.NET 2008 ist zu 100% in Ordnung. Siehe "Bearbeiten # 2" weiter unten.] Möglicherweise gibt es andere .NET-Sprachen, bei denen ebenfalls ein Problem auftritt. Ich kann nicht sicher sein.
Ich sehe mich jedoch nicht für eine andere Sprache als C # oder Visual Basic .NET entwickelt, und es macht mir nichts aus, sie auf C # und VB.NET für .NET Framework 3.0 und höher zu beschränken. (Ich könnte mir nicht vorstellen, zu diesem Zeitpunkt auf 2.0 zurückzukehren, um ehrlich zu sein.)
Kann sich noch jemand ein Problem damit vorstellen? Oder bricht dies einfach so sehr mit der Konvention, dass sich die Mägen der Menschen drehen?
Hier sind einige verwandte Links, die ich gefunden habe:
(1) Richtlinien für das Event-Design [MSDN 3.5]
(3) Ereignissignaturmuster in .net [StackOverflow 2008]
Ich interessiere mich für jedermanns und jedermanns Meinung dazu ...
Danke im Voraus,
Mike
Edit # 1: Dies ist eine Antwort auf den Beitrag von Tommy Carlier :
Hier ist ein voll funktionsfähiges Beispiel, das zeigt, dass sowohl stark typisierte Ereignishandler als auch die aktuellen Standardereignishandler, die einen Parameter "Objektabsender" verwenden, mit diesem Ansatz koexistieren können. Sie können den Code kopieren, einfügen und ausführen:
namespace csScrap.GenericEventHandling
{
class PublisherEventArgs : EventArgs
{
// ...
}
[SerializableAttribute]
public delegate void StrongTypedEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
class Publisher
{
public event StrongTypedEventHandler<Publisher, PublisherEventArgs> SomeEvent;
public void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent(this, new PublisherEventArgs());
}
}
}
class StrongTypedSubscriber
{
public void SomeEventHandler(Publisher sender, PublisherEventArgs e)
{
MessageBox.Show("StrongTypedSubscriber.SomeEventHandler called.");
}
}
class TraditionalSubscriber
{
public void SomeEventHandler(object sender, PublisherEventArgs e)
{
MessageBox.Show("TraditionalSubscriber.SomeEventHandler called.");
}
}
class Tester
{
public static void Main()
{
Publisher publisher = new Publisher();
StrongTypedSubscriber strongTypedSubscriber = new StrongTypedSubscriber();
TraditionalSubscriber traditionalSubscriber = new TraditionalSubscriber();
publisher.SomeEvent += strongTypedSubscriber.SomeEventHandler;
publisher.SomeEvent += traditionalSubscriber.SomeEventHandler;
publisher.OnSomeEvent();
}
}
}
Edit # 2: Dies ist eine Antwort auf Andrew Hares Aussage bezüglich Kovarianz und Kontravarianz und wie sie hier gilt. Delegierte in der C # -Sprache hatten so lange Kovarianz und Kontravarianz, dass es sich nur "intrinsisch" anfühlt, aber nicht. Ich weiß nicht, dass es möglicherweise sogar in der CLR aktiviert ist, aber Visual Basic .NET hat für seine Delegaten erst mit .NET Framework 3.0 (VB.NET 2008) Kovarianz- und Kontravarianzfunktionen erhalten. Infolgedessen kann Visual Basic.NET für .NET 2.0 und niedriger diesen Ansatz nicht verwenden.
Das obige Beispiel kann beispielsweise wie folgt in VB.NET übersetzt werden:
Namespace GenericEventHandling
Class PublisherEventArgs
Inherits EventArgs
' ...
' ...
End Class
<SerializableAttribute()> _
Public Delegate Sub StrongTypedEventHandler(Of TSender, TEventArgs As EventArgs) _
(ByVal sender As TSender, ByVal e As TEventArgs)
Class Publisher
Public Event SomeEvent As StrongTypedEventHandler(Of Publisher, PublisherEventArgs)
Public Sub OnSomeEvent()
RaiseEvent SomeEvent(Me, New PublisherEventArgs)
End Sub
End Class
Class StrongTypedSubscriber
Public Sub SomeEventHandler(ByVal sender As Publisher, ByVal e As PublisherEventArgs)
MessageBox.Show("StrongTypedSubscriber.SomeEventHandler called.")
End Sub
End Class
Class TraditionalSubscriber
Public Sub SomeEventHandler(ByVal sender As Object, ByVal e As PublisherEventArgs)
MessageBox.Show("TraditionalSubscriber.SomeEventHandler called.")
End Sub
End Class
Class Tester
Public Shared Sub Main()
Dim publisher As Publisher = New Publisher
Dim strongTypedSubscriber As StrongTypedSubscriber = New StrongTypedSubscriber
Dim traditionalSubscriber As TraditionalSubscriber = New TraditionalSubscriber
AddHandler publisher.SomeEvent, AddressOf strongTypedSubscriber.SomeEventHandler
AddHandler publisher.SomeEvent, AddressOf traditionalSubscriber.SomeEventHandler
publisher.OnSomeEvent()
End Sub
End Class
End Namespace
VB.NET 2008 kann zu 100% ausgeführt werden. Aber ich habe es jetzt auf VB.NET 2005 getestet, nur um sicherzugehen, und es wird nicht kompiliert.
Die Methode 'Public Sub SomeEventHandler (Absender als Objekt, e als vbGenericEventHandling.GenericEventHandling.PublisherEventArgs)' hat nicht dieselbe Signatur wie der Delegat 'Delegate Sub StrongTypedEventHandler (von TSender, TEventArgs als System.EventArgs) (Absender als Herausgeber, e als PublisherEvent) '
Grundsätzlich sind Delegaten in VB.NET-Versionen 2005 und niedriger unveränderlich. Ich habe vor ein paar Jahren tatsächlich an diese Idee gedacht, aber die Unfähigkeit von VB.NET, damit umzugehen, hat mich gestört ... Aber ich bin jetzt fest zu C # übergegangen, und VB.NET kann jetzt damit umgehen dieser Beitrag.
Bearbeiten: Update # 3
Ok, ich benutze das jetzt schon eine Weile ziemlich erfolgreich. Es ist wirklich ein schönes System. Ich habe beschlossen, meinen "StrongTypedEventHandler" als "GenericEventHandler" zu bezeichnen, der wie folgt definiert ist:
[SerializableAttribute]
public delegate void GenericEventHandler<TSender, TEventArgs>(
TSender sender,
TEventArgs e
)
where TEventArgs : EventArgs;
Abgesehen von dieser Umbenennung habe ich sie genau wie oben beschrieben implementiert.
Es wird über die FxCop-Regel CA1009 ausgelöst, die besagt:
"Konventionell haben .NET-Ereignisse zwei Parameter, die den Ereignisabsender und die Ereignisdaten angeben. Ereignishandlersignaturen sollten folgendermaßen aussehen: void MyEventHandler (Objektabsender, EventArgs e). Der Parameter 'sender' ist immer vom Typ System.Object, Selbst wenn es möglich ist, einen spezifischeren Typ zu verwenden. Der Parameter 'e' ist immer vom Typ System.EventArgs. Ereignisse, die keine Ereignisdaten bereitstellen, sollten den Delegatentyp System.EventHandler verwenden. Ereignishandler geben void zurück, damit sie senden können jedes Ereignis an mehrere Zielmethoden. Jeder von einem Ziel zurückgegebene Wert geht nach dem ersten Aufruf verloren. "
Natürlich wissen wir das alles und brechen trotzdem die Regeln. (Alle Ereignishandler können in jedem Fall den Standard-Objektabsender in ihrer Signatur verwenden, wenn dies bevorzugt wird. Dies ist eine unveränderliche Änderung.)
Die Verwendung von a SuppressMessageAttribute
macht also den Trick:
[SuppressMessage("Microsoft.Design", "CA1009:DeclareEventHandlersCorrectly",
Justification = "Using strong-typed GenericEventHandler<TSender, TEventArgs> event handler pattern.")]
Ich hoffe, dass dieser Ansatz irgendwann in der Zukunft zum Standard wird. Es funktioniert wirklich sehr gut.
Vielen Dank für all Ihre Meinungen, Leute, ich weiß das wirklich zu schätzen ...
Mike
oh hi this my hom work solve it plz :code dump:
Fragen in Tweet-Größe , sondern eine Frage , aus der wir lernen .EventHandler<,>
alsGenericEventHandler<,>
.EventHandler<>
In BCL gibt es bereits Generika, die nur EventHandler heißen. EventHandler ist also ein gebräuchlicherer Name und Delegierte unterstützenAntworten:
Es scheint, dass Microsoft dies aufgegriffen hat, da ein ähnliches Beispiel jetzt auf MSDN verfügbar ist:
Generische Delegierte
quelle
Was Sie vorschlagen, macht tatsächlich viel Sinn, und ich frage mich nur, ob dies eines dieser Dinge ist, das einfach so ist, weil es ursprünglich vor Generika entworfen wurde, oder ob es einen wirklichen Grund dafür gibt.
quelle
Die Windows-Laufzeit (WinRT) führt einen
TypedEventHandler<TSender, TResult>
Delegaten ein, der genau das tut, was SieStrongTypedEventHandler<TSender, TResult>
tun, aber anscheinend ohne die Einschränkung desTResult
Typparameters:Die MSDN-Dokumentation finden Sie hier .
quelle
EventArgs
, es ist nur eine Konventionssacheargs
wird ausdrücklich angegeben, dassnull
keine Ereignisdaten vorhanden sind. Es scheint also, dass sie standardmäßig kein im Wesentlichen leeres Objekt verwenden. Ich vermute, dass die ursprüngliche Idee war, dass eine Methode mit einem zweiten Parameter vom TypEventArgs
jedes Ereignis verarbeiten kann, da die Typen immer kompatibel sind. Sie erkennen jetzt wahrscheinlich, dass es nicht so wichtig ist, mehrere verschiedene Ereignisse mit einer Methode verarbeiten zu können.Ich habe Probleme mit den folgenden Aussagen:
Erstens hat nichts, was Sie hier getan haben, etwas mit Kovarianz oder Kontravarianz zu tun.( Bearbeiten: Die vorherige Aussage ist falsch. Weitere Informationen finden Sie unter Kovarianz und Kontravarianz in Delegaten. ) Diese Lösung funktioniert in allen CLR-Versionen 2.0 und höher einwandfrei (dies funktioniert natürlich nicht in einer CLR 1.0-Anwendung, da Generika verwendet werden).Zweitens stimme ich überhaupt nicht zu, dass Ihre Idee an "Blasphemie" grenzt, da dies eine wunderbare Idee ist.
quelle
Ich warf einen Blick darauf, wie dies mit dem neuen WinRT und basierend auf anderen Meinungen hier gehandhabt wurde, und entschied mich schließlich dafür, es so zu machen:
Dies scheint der beste Weg zu sein, wenn man den Namen TypedEventHandler in WinRT verwendet.
quelle
Ich denke, es ist eine großartige Idee, und MS hat möglicherweise einfach nicht die Zeit oder das Interesse, in eine Verbesserung zu investieren, wie zum Beispiel beim Wechsel von ArrayList zu generischen Listen.
quelle
Soweit ich weiß, soll sich das Feld "Absender" immer auf das Objekt beziehen, das das Ereignisabonnement enthält. Wenn ich meine Dr. Wenn einer die eigentliche Arbeit erledigt und die eigentlichen Daten enthält und der andere einen öffentlichen Schnittstellen-Wrapper bereitstellt, könnte der Hauptteil einen schwachen Verweis auf den Wrapper-Teil enthalten. Wenn der Wrapper-Teil durch Müll gesammelt wird, würde dies bedeuten Es war niemand mehr an den gesammelten Daten interessiert, und der Änderungslogger sollte sich daher von jedem empfangenen Ereignis abmelden.
Da es möglich ist, dass ein Objekt Ereignisse im Namen eines anderen Objekts sendet, kann ich einen potenziellen Nutzen erkennen, wenn ein "Absender" -Feld vom Objekttyp vorhanden ist und das von EventArgs abgeleitete Feld einen Verweis auf das Objekt enthält, das sollte gehandelt werden. Die Nützlichkeit des Felds "Absender" wird jedoch wahrscheinlich durch die Tatsache eingeschränkt, dass es keine saubere Möglichkeit für ein Objekt gibt, sich von einem unbekannten Absender abzumelden.
(*) Eine sauberere Methode zur Behandlung von Abmeldungen wäre ein Multicast-Delegatentyp für Funktionen, die Boolean zurückgeben. Wenn eine von einem solchen Delegaten aufgerufene Funktion True zurückgibt, wird der Delegat gepatcht, um dieses Objekt zu entfernen. Dies würde bedeuten, dass Delegaten nicht mehr wirklich unveränderlich sind, aber es sollte möglich sein, eine solche Änderung threadsicher durchzuführen (z. B. indem die Objektreferenz auf Null gesetzt wird und der Multicast-Delegatencode alle eingebetteten Nullobjektreferenzen ignoriert). In diesem Szenario kann ein Versuch, ein veröffentlichtes Objekt zu veröffentlichen und ein Ereignis zu erstellen, sehr sauber behandelt werden, unabhängig davon, woher das Ereignis stammt.
quelle
Wenn man auf Blasphemie als einzigen Grund zurückblickt, Absender zu einem Objekttyp zu machen (um Probleme mit Kontravarianzen im VB 2005-Code, der meiner Meinung nach ein Fehler von Microsoft ist, wegzulassen), kann jemand zumindest ein theoretisches Motiv vorschlagen, um das zweite Argument dem EventArgs-Typ zuzuordnen. Gibt es in diesem speziellen Fall einen guten Grund, die Richtlinien und Konventionen von Microsoft einzuhalten?
Es scheint seltsam, einen anderen EventArgs-Wrapper für andere Daten entwickeln zu müssen, die wir im Event-Handler übergeben möchten. Warum können diese Daten dort nicht direkt übergeben werden? Betrachten Sie die folgenden Codeabschnitte
[Beispiel 1]
[Beispiel 2]
quelle
In der aktuellen Situation (Absender ist Objekt) können Sie problemlos eine Methode an mehrere Ereignisse anhängen:
Wenn der Absender generisch wäre, wäre das Ziel des Klickereignisses nicht vom Typ Button oder Label, sondern vom Typ Control (da das Ereignis in Control definiert ist). Einige Ereignisse in der Button-Klasse hätten also ein Ziel vom Typ Control, andere andere Zieltypen.
quelle
Ich glaube nicht, dass irgendetwas falsch ist mit dem, was du tun willst. Ich vermute größtenteils, dass der
object sender
Parameter erhalten bleibt, um weiterhin Code vor 2.0 zu unterstützen.Wenn Sie diese Änderung wirklich für eine öffentliche API vornehmen möchten, sollten Sie eine eigene Basis-EvenArgs-Klasse erstellen. Etwas wie das:
Dann können Sie Ihre Ereignisse so deklarieren
Und Methoden wie diese:
wird weiterhin abonnieren können.
BEARBEITEN
Diese letzte Zeile hat mich ein wenig nachdenken lassen ... Sie sollten tatsächlich in der Lage sein, das, was Sie vorschlagen, zu implementieren, ohne externe Funktionen zu beeinträchtigen, da die Laufzeit kein Problem mit Downcasting-Parametern hat. Ich würde mich immer noch
DataEventArgs
(persönlich) zur Lösung neigen. Ich würde dies jedoch tun, obwohl ich weiß, dass es redundant ist, da der Absender im ersten Parameter und als Eigenschaft der Ereignisargumente gespeichert ist.Ein Vorteil des Festhaltens an
DataEventArgs
besteht darin, dass Sie Ereignisse verketten und den Absender ändern können (um den letzten Absender darzustellen), während die EventArgs den ursprünglichen Absender beibehalten.quelle
Tue es. Bei nicht komponentenbasiertem Code vereinfache ich Ereignissignaturen häufig einfach
wo
MyEventType
erbt nicht vonEventArgs
. Warum sich die Mühe machen, wenn ich nie beabsichtige, eines der Mitglieder von EventArgs zu verwenden?quelle
event Action<S, T>
,event Action<R, S, T>
usw. Ich habe eine Erweiterungsmethode , umRaise
sie Thread-sicher :)