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