internal static Task FromEvent <T>(this T obj, string eventName)
    {
        var        tcs                 = new TaskCompletionSource <object>();
        var        tcsh                = new TaskCompletionSourceHolder <object>(tcs, null);
        Type       tcshType            = tcsh.GetType();
        MethodInfo setResultMethodInfo = tcshType.GetMethod(
            "SetResult", BindingFlags.NonPublic | BindingFlags.Instance);
        EventInfo   eventInfo         = obj.GetType().GetEvent(eventName);
        Type        eventDelegateType = eventInfo.EventHandlerType;
        Type        returnType;
        List <Type> parameterTypes;

        GetDelegateParameterAndReturnTypes(eventDelegateType,
                                           out parameterTypes, out returnType);
        if (returnType != typeof(void))
        {
            throw new NotSupportedException();
        }
        // I'm going to create an instance-like method
        // so, first parameter should be of instance type.
        // I.e. TaskCompletionSourceHolder<object> this
        parameterTypes.Insert(0, tcshType);
        DynamicMethod handler = new DynamicMethod("unnamed",
                                                  returnType, parameterTypes.ToArray(), tcshType);
        ILGenerator ilgen = handler.GetILGenerator();

        // load zero-argument (i.e. *this*) onto the evalution stack
        ilgen.Emit(OpCodes.Ldarg_0);
        // call this.SetResult();
        ilgen.EmitCall(OpCodes.Call, setResultMethodInfo, null);
        // and return
        ilgen.Emit(OpCodes.Ret);
        Delegate dEmitted = handler.CreateDelegate(eventDelegateType, tcsh);

        tcsh.Target    = obj;
        tcsh.EventInfo = eventInfo;
        tcsh.Delegate  = dEmitted;
        eventInfo.AddEventHandler(obj, dEmitted);
        return(tcs.Task);
    }
    public static Task <object[]> FromEvent <T>(this T obj, string eventName)
    {
        var        tcs                 = new TaskCompletionSource <object[]>();
        var        tcsh                = new TaskCompletionSourceHolder(tcs);
        Type       tcshType            = tcsh.GetType();
        MethodInfo setResultMethodInfo = tcshType.GetMethod(
            "SetResult", BindingFlags.NonPublic | BindingFlags.Instance);
        EventInfo     eventInfo         = obj.GetType().GetEvent(eventName);
        Type          eventDelegateType = eventInfo.EventHandlerType;
        DynamicMethod handler;

        if (!s_emittedHandlers.TryGetValue(eventDelegateType, out handler))
        {
            Type        returnType;
            List <Type> parameterTypes;
            GetDelegateParameterAndReturnTypes(eventDelegateType,
                                               out parameterTypes, out returnType);
            if (returnType != typeof(void))
            {
                throw new NotSupportedException();
            }
            // I'm going to create an instance-like method
            // so, first argument must an instance itself
            // i.e. TaskCompletionSourceHolder<object> *this*
            parameterTypes.Insert(0, tcshType);
            Type[] parameterTypesAr = parameterTypes.ToArray();
            handler = new DynamicMethod("unnamed",
                                        returnType, parameterTypesAr, tcshType);
            ILGenerator ilgen = handler.GetILGenerator();
            // declare local variable of type object[]
            LocalBuilder arr = ilgen.DeclareLocal(typeof(object[]));
            // push array size onto the stack
            ilgen.Emit(OpCodes.Ldc_I4, parameterTypesAr.Length - 1);
            // create an array
            ilgen.Emit(OpCodes.Newarr, typeof(object));
            // and store it in the local variable
            ilgen.Emit(OpCodes.Stloc, arr);
            // iterate thru all arguments except the zero one (i.e. *this*)
            for (int i = 1; i < parameterTypes.Count; i++)
            {
                // push array onto the stack
                ilgen.Emit(OpCodes.Ldloc, arr);
                // push argument's index onto the stack
                ilgen.Emit(OpCodes.Ldc_I4, i - 1);
                // push argument onto the stack
                ilgen.Emit(OpCodes.Ldarg, i);
                // check if it is of a value type
                // and perform boxing if needed
                if (parameterTypes[i].IsValueType)
                {
                    ilgen.Emit(OpCodes.Box, parameterTypes[i]);
                }
                // store the value to array
                ilgen.Emit(OpCodes.Stelem, typeof(object));
            }
            // load zero-argument (i.e. *this*) onto the stack
            ilgen.Emit(OpCodes.Ldarg_0);
            // load arguments array onto the stack
            ilgen.Emit(OpCodes.Ldloc, arr);
            // call this.SetResult(arr);
            ilgen.Emit(OpCodes.Call, setResultMethodInfo);
            // and return
            ilgen.Emit(OpCodes.Ret);
            s_emittedHandlers.Add(eventDelegateType, handler);
        }
        Delegate dEmitted = handler.CreateDelegate(eventDelegateType, tcsh);

        tcsh.Target    = obj;
        tcsh.EventInfo = eventInfo;
        tcsh.Delegate  = dEmitted;
        eventInfo.AddEventHandler(obj, dEmitted);
        return(tcs.Task);
    }