Allgemeine FromEvent-Methode

Mit dem neuen Async / Warten-Modell ist es ziemlich einfach, a zu generierenTask Dies ist abgeschlossen, wenn ein Ereignis ausgelöst wird. Sie müssen nur diesem Muster folgen:

public class MyClass
{
    public event Action OnCompletion;
}

public static Task FromEvent(MyClass obj)
{
    TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();

    obj.OnCompletion += () =>
        {
            tcs.SetResult(null);
        };

    return tcs.Task;
}

Dies ermöglicht dann:

await FromEvent(new MyClass());

Das Problem ist, dass Sie ein neues erstellen müssenFromEvent Methode für jedes Ereignis in jeder Klasse, die Sie möchtenawait auf. Das könnte sehr schnell sehr groß werden, und es ist sowieso meistens nur Kessel-Code.

Im Idealfall möchte ich in der Lage sein, so etwas zu tun:

await FromEvent(new MyClass().OnCompletion);

Dann könnte ich das selbe wiederverwendenFromEvent Methode für jedes Ereignis in jeder Instanz. Ich habe einige Zeit damit verbracht, eine solche Methode zu erstellen, und es gibt eine Reihe von Fehlern. Für den obigen Code wird der folgende Fehler generiert:

Das Ereignis 'Namespace.MyClass.OnCompletion' kann nur auf der linken Seite von + = oder - = angezeigt werden

Soweit ich das beurteilen kann, wird es niemals eine Möglichkeit geben, das Ereignis so durch Code zu führen.

Das nächstbeste schien also zu sein, den Ereignisnamen als Zeichenfolge zu übergeben:

await FromEvent(new MyClass(), "OnCompletion");

Es ist nicht so ideal; Sie erhalten kein Verständnis und würden einen Laufzeitfehler erhalten, wenn das Ereignis für diesen Typ nicht vorhanden ist, aber es könnte dennoch nützlicher sein als Tonnen von FromEvent-Methoden.

So ist es einfach genug, Reflexion und zu verwendenGetEvent(eventName) um das zu bekommenEventInfo Objekt. Das nächste Problem ist, dass der Delegat dieses Ereignisses zur Laufzeit nicht bekannt ist (und variieren muss). Das macht das Hinzufügen eines Ereignishandlers schwierig, da wir zur Laufzeit dynamisch eine Methode erstellen müssen, die mit einer bestimmten Signatur übereinstimmt (aber alle Parameter ignoriert), die auf a zugreiftTaskCompletionSource das haben wir schon und setzt sein ergebnis.

Zum Glück habe ich gefundendieser Link die Anweisungen enthält, wie man [fast] genau das über machtReflection.Emit. Jetzt ist das Problem, dass wir IL ausgeben müssen, und ich habe keine Ahnung, wie ich auf das zugreifen solltcs Beispiel, das ich habe.

Nachfolgend sind die Fortschritte aufgeführt, die ich bei der Fertigstellung erzielt habe:

public static Task FromEvent<T>(this T obj, string eventName)
{
    var tcs = new TaskCompletionSource<object>();
    var eventInfo = obj.GetType().GetEvent(eventName);

    Type eventDelegate = eventInfo.EventHandlerType;

    Type[] parameterTypes = GetDelegateParameterTypes(eventDelegate);
    DynamicMethod handler = new DynamicMethod("unnamed", null, parameterTypes);

    ILGenerator ilgen = handler.GetILGenerator();

    //TODO ilgen.Emit calls go here

    Delegate dEmitted = handler.CreateDelegate(eventDelegate);

    eventInfo.AddEventHandler(obj, dEmitted);

    return tcs.Task;
}

Was IL könnte ich möglicherweise ausgeben, das mir erlauben würde, das Ergebnis von einzustellenTaskCompletionSource? Oder gibt es alternativ einen anderen Ansatz zum Erstellen einer Methode, die eine Aufgabe für ein beliebiges Ereignis von einem beliebigen Typ zurückgibt?

Antworten auf die Frage(3)

Ihre Antwort auf die Frage