Metoda ogólnego przeznaczenia FromEvent

Korzystając z nowego modelu async / await, generowanie a jest dość prosteTask to jest zakończone, gdy wydarzy się zdarzenie; po prostu musisz podążać za tym wzorem:

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;
}

Pozwala to na:

await FromEvent(new MyClass());

Problem polega na tym, że musisz stworzyć nowyFromEvent metoda dla każdego zdarzenia w każdej klasie, którą chceszawait na. To naprawdę może być naprawdę szybkie, a mimo to jest to po prostu kod podstawowy.

Idealnie chciałbym móc zrobić coś takiego:

await FromEvent(new MyClass().OnCompletion);

Wtedy mógłbym ponownie użyć tego samegoFromEvent metoda dla dowolnego zdarzenia w dowolnej instancji. Spędziłem trochę czasu, próbując stworzyć taką metodę, i istnieje wiele problemów. Dla powyższego kodu wygeneruje następujący błąd:

Zdarzenie „Namespace.MyClass.OnCompletion” może pojawić się tylko po lewej stronie + = lub - =

O ile wiem, nigdy nie będzie sposobu na przekazanie tego wydarzenia przez kod.

Tak więc następną najlepszą rzeczą wydawało się próbować przekazać nazwę zdarzenia jako ciąg:

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

To nie jest tak idealne; nie dostaniesz intellisense i dostaniesz błąd runtime, jeśli zdarzenie nie istnieje dla tego typu, ale nadal może być bardziej użyteczne niż mnóstwo metod FromEvent.

Łatwo więc użyć refleksji iGetEvent(eventName) aby uzyskaćEventInfo obiekt. Następnym problemem jest to, że delegat tego zdarzenia nie jest znany (i musi być w stanie się różnić) w czasie wykonywania. To sprawia, że ​​dodawanie procedury obsługi zdarzeń jest trudne, ponieważ musimy dynamicznie tworzyć metodę w czasie wykonywania, dopasowując daną sygnaturę (ale ignorując wszystkie parametry), która uzyskuje dostęp doTaskCompletionSource które już mamy i ustala jego wynik.

Na szczęście znalazłemten link który zawiera instrukcje, jak wykonać [prawie] dokładnie to przezReflection.Emit. Teraz problem polega na tym, że musimy wyemitować IL i nie mam pojęcia, jak uzyskać dostęp dotcs przykład, który mam.

Poniżej przedstawiono postępy, jakie poczyniłem w kierunku ukończenia tego:

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;
}

Jaką IL mógłbym emitować, co pozwoliłoby mi ustawić wynikTaskCompletionSource? Lub alternatywnie, czy istnieje inne podejście do tworzenia metody, która zwraca Zadanie dla dowolnego zdarzenia z dowolnego typu?

questionAnswers(3)

yourAnswerToTheQuestion