Mit dem neuen async / await-Modell ist es relativ einfach, ein Task
zu generieren, das beim Auslösen eines Ereignisses abgeschlossen wird. Sie müssen nur diesem Muster folgen:
Dies ermöglicht dann:
%Vor% Das Problem besteht darin, dass Sie für jedes Ereignis in jeder Klasse, für die Sie FromEvent
verwenden möchten, eine neue await
-Methode erstellen müssen. Das könnte sehr schnell sehr groß werden, und es ist sowieso nur ein Vorabcode.
Idealerweise würde ich gerne so etwas machen können:
%Vor% Dann könnte ich die gleiche FromEvent
-Methode für jedes Event in jeder Instanz wiederverwenden. Ich habe einige Zeit damit verbracht, eine solche Methode zu erstellen, und es gibt eine Reihe von Hindernissen. Für den obigen Code wird der folgende Fehler generiert:
Das Ereignis 'Namespace.MyClass.OnCompletion' kann nur auf der linken Seite von + = oder - =
erscheinen
Soweit ich das beurteilen kann, wird es niemals eine Möglichkeit geben, die Veranstaltung so durch den Code zu führen.
Die nächstbeste Sache schien zu sein, den Ereignisnamen als String zu übergeben:
%Vor%Es ist nicht so ideal; Sie erhalten kein Intellisense und erhalten einen Laufzeitfehler, wenn das Ereignis für diesen Typ nicht existiert, aber es könnte immer noch nützlicher sein als Tonnen von FromEvent-Methoden.
Es ist also einfach genug, reflection und GetEvent(eventName)
zu verwenden, um das Objekt EventInfo
zu erhalten. Das nächste Problem ist, dass der Delegat dieses Ereignisses zur Laufzeit nicht bekannt ist (und in der Lage sein muss, zu variieren). Das erschwert das Hinzufügen eines Event-Handlers, da wir zur Laufzeit dynamisch eine Methode erstellen müssen, die eine bestimmte Signatur (aber alle Parameter ignorieren) berücksichtigt, die auf ein bereits vorhandenes TaskCompletionSource
zugreift und das Ergebnis festlegt.
Glücklicherweise habe ich diesen Link gefunden, der Anweisungen dazu enthält, wie [fast] zu tun ist. genau das via Reflection.Emit
. Jetzt ist das Problem, dass wir IL ausstrahlen müssen, und ich habe keine Ahnung, wie ich auf die tcs
Instanz zugreifen kann, die ich habe.
Unten ist der Fortschritt, den ich gemacht habe, um dies zu beenden:
%Vor% Welche IL könnte ich ausgeben, damit ich das Ergebnis von TaskCompletionSource
festlegen kann? Oder gibt es alternativ einen anderen Ansatz zum Erstellen einer Methode, die einen Task für ein beliebiges Ereignis von einem beliebigen Typ zurückgibt?
Hier geht's:
%Vor%Dieser Code funktioniert für fast alle Ereignisse, die void zurückgeben (unabhängig von der Parameterliste).
Es kann verbessert werden, um ggf. Rückgabewerte zu unterstützen.
Sie können den Unterschied zwischen Dax's und meinen Methoden unten sehen:
%Vor% Kurz gesagt, mein Code unterstützt wirklich jede Art von Delegattyp. Sie sollten (und müssen nicht) explizit wie TaskFromEvent<int, string>
angeben.
Dies wird Ihnen geben, was Sie brauchen, ohne dass Sie etwas machen müssen, und viel einfacher. Es funktioniert mit jeder Art von Event-Delegierten; Sie müssen nur einen anderen Handler für jede Anzahl von Parametern in Ihrem Ereignisdelegaten erstellen. Unten finden Sie die Handler, die Sie für 0..2 benötigen. Dies sollte die überwiegende Mehrheit Ihrer Anwendungsfälle sein. Die Erweiterung auf 3 und höher ist ein einfaches Kopieren und Einfügen aus der 2-Parameter-Methode.
Dies ist auch mächtiger als die ilgen-Methode, da Sie alle vom Ereignis in Ihrem asynchronen Muster erzeugten Werte verwenden können.
%Vor%Verwendung wäre so. Wie Sie sehen, funktioniert das Ereignis, obwohl es in einem benutzerdefinierten Delegaten definiert ist. Und Sie können die gerundeten Werte als Tupel erfassen.
%Vor%Hier ist eine Hilfsfunktion , mit der Sie die TaskFromEvent-Funktionen in jeweils nur einer Zeile schreiben können Drei Methoden sind zu viel Kopieren und Einfügen für Ihre Einstellungen. Kredit muss an max gegeben werden, um das zu vereinfachen, was ich ursprünglich hatte.
Wenn Sie pro Delegatentyp eine Methode verwenden möchten, können Sie Folgendes tun:
%Vor%Sie würden es wie:
verwenden %Vor%Beachten Sie, dass Sie sich auf diese Weise niemals von der Veranstaltung abmelden, was für Sie möglicherweise ein Problem darstellt.
Wenn Sie generische Delegaten verwenden, genügt für jeden generischen Typ eine Methode, für die Sie keinen konkreten Typ benötigen:
%Vor% Obwohl Typinferenz nicht damit arbeitet, müssen Sie den Typparameter explizit angeben (unter der Annahme, dass der Typ von OnCompletion
hier Action<string>
ist):
Tags und Links c# task-parallel-library async-await