Método de uso geral FromEvent

Usando o novo modelo async / await é bastante simples gerar umTask que é concluído quando um evento é disparado; você só precisa seguir esse padrão:

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

Isto então permite:

await FromEvent(new MyClass());

O problema é que você precisa criar um novoFromEvent método para cada evento em todas as classes que você gostaria deawait em. Isso pode ficar muito grande muito rápido, e é basicamente apenas código clichê.

Idealmente eu gostaria de poder fazer algo assim:

await FromEvent(new MyClass().OnCompletion);

Então eu poderia reutilizar o mesmoFromEvent método para qualquer evento em qualquer instância. Eu passei algum tempo tentando criar um método desses, e há vários obstáculos. Para o código acima, irá gerar o seguinte erro:

O evento 'Namespace.MyClass.OnCompletion' só pode aparecer no lado esquerdo de + = ou - =

Tanto quanto eu posso dizer, nunca haverá uma maneira de passar o evento assim através do código.

Então, a próxima melhor coisa parecia estar tentando passar o nome do evento como uma string:

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

Não é tão ideal; você não obtém intellisense e receberia um erro de tempo de execução se o evento não existisse para esse tipo, mas ainda assim poderia ser mais útil do que toneladas de métodos FromEvent.

Então é fácil usar reflexão eGetEvent(eventName) para obter oEventInfo objeto. O próximo problema é que o delegado desse evento não é conhecido (e precisa ser capaz de variar) em tempo de execução. Isso torna a adição de um manipulador de eventos difícil, porque precisamos criar dinamicamente um método em tempo de execução, correspondendo a uma determinada assinatura (mas ignorando todos os parâmetros) que acessa umTaskCompletionSource que já temos e define seu resultado.

Felizmente eu encontreiesse link que contém instruções sobre como fazer [quase] exatamente isso viaReflection.Emit. Agora o problema é que precisamos emitir IL, e não tenho idéia de como acessar otcs exemplo que eu tenho.

Abaixo está o progresso que fiz para terminar isso:

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

Qual IL eu poderia emitir que me permitiria definir o resultado doTaskCompletionSource? Ou, alternativamente, existe outra abordagem para criar um método que retorna uma tarefa para qualquer evento arbitrário de um tipo arbitrário?

questionAnswers(3)

yourAnswerToTheQuestion