Общий метод FromEvent

Используя новую модель async / await, довольно просто сгенерироватьTask это завершается, когда происходит событие; вам просто нужно следовать этой схеме:

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

Это тогда позволяет:

await FromEvent(new MyClass());

Проблема в том, что вам нужно создать новыйFromEvent&nbsp;метод для каждого события в каждом классе, который вы хотели быawait&nbsp;на. Это может быть очень большим и очень быстрым, и в любом случае это в основном просто шаблонный код.

В идеале я хотел бы иметь возможность сделать что-то вроде этого:

await FromEvent(new MyClass().OnCompletion);

Тогда я мог бы снова использовать тот жеFromEvent&nbsp;метод для любого события в любом случае. Я потратил некоторое время, пытаясь создать такой метод, и есть много препятствий. Для приведенного выше кода будет сгенерирована следующая ошибка:

Событие Namespace.MyClass.OnCompletion может появляться только в левой части + = или - =

Насколько я могу судить, никогда не будет способа передать подобное событие через код.

Итак, следующая лучшая вещь, казалось, пыталась передать имя события в виде строки:

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

Это не так идеально; вы не получите intellisense и получите ошибку времени выполнения, если событие не существует для этого типа, но оно все равно может быть более полезным, чем множество методов FromEvent.

Так что достаточно легко использовать отражение иGetEvent(eventName)&nbsp;чтобы получитьEventInfo&nbsp;объект. Следующая проблема заключается в том, что делегат этого события неизвестен (и должен иметь возможность изменяться) во время выполнения. Это затрудняет добавление обработчика событий, потому что нам нужно динамически создавать метод во время выполнения, сопоставляя данную сигнатуру (но игнорируя все параметры), которая обращается кTaskCompletionSource&nbsp;что у нас уже есть и устанавливает свой результат.

К счастью я нашелэта ссылка&nbsp;который содержит инструкции о том, как сделать [почти] именно черезReflection.Emit, Теперь проблема в том, что нам нужно излучать IL, и я понятия не имею, как получить доступ кtcs&nbsp;экземпляр у меня есть.

Ниже приведен прогресс, который я сделал в завершении этого:

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

Какой ИЛ я мог бы испустить, что позволило бы мне установить результатTaskCompletionSource? Или, альтернативно, есть другой подход к созданию метода, который возвращает Задачу для любого произвольного события из произвольного типа?