Advertisement
nikolay_khil

General purpose FromEvent method (for Windows Phone)

Oct 18th, 2012
265
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 5.43 KB | None | 0 0
  1.     public class TaskCompletionSourceHolder
  2.     {
  3.         private readonly TaskCompletionSource<object[]> m_tcs;
  4.  
  5.         internal object Target { get; set; }
  6.         internal EventInfo EventInfo { get; set; }
  7.         internal Delegate Delegate { get; set; }
  8.  
  9.         internal TaskCompletionSourceHolder(TaskCompletionSource<object[]> tsc)
  10.         {
  11.             m_tcs = tsc;
  12.         }
  13.  
  14.         public void SetResult(params object[] args)
  15.         {
  16.             // this method will be called from emitted IL
  17.             // so we can set result here, unsubscribe from the event
  18.             // or do whatever we want.
  19.  
  20.             // object[] args will contain arguments
  21.             // passed to the event handler
  22.             m_tcs.SetResult(args);
  23.             EventInfo.RemoveEventHandler(Target, Delegate);
  24.         }
  25.     }
  26.  
  27.     public static class ExtensionMethods
  28.     {
  29.         private static Dictionary<Type, DynamicMethod> s_emittedHandlers =
  30.             new Dictionary<Type, DynamicMethod>();
  31.  
  32.         private static void GetDelegateParameterAndReturnTypes(Type delegateType,
  33.             out List<Type> parameterTypes, out Type returnType)
  34.         {
  35.             if (delegateType.BaseType != typeof(MulticastDelegate))
  36.                 throw new ArgumentException("delegateType is not a delegate");
  37.  
  38.             MethodInfo invoke = delegateType.GetMethod("Invoke");
  39.             if (invoke == null)
  40.                 throw new ArgumentException("delegateType is not a delegate.");
  41.  
  42.             ParameterInfo[] parameters = invoke.GetParameters();
  43.             parameterTypes = new List<Type>(parameters.Length);
  44.             for (int i = 0; i < parameters.Length; i++)
  45.                 parameterTypes.Add(parameters[i].ParameterType);
  46.  
  47.             returnType = invoke.ReturnType;
  48.         }
  49.  
  50.         public static Task<object[]> FromEvent<T>(this T obj, string eventName)
  51.         {
  52.             var tcs = new TaskCompletionSource<object[]>();
  53.             var tcsh = new TaskCompletionSourceHolder(tcs);
  54.  
  55.             EventInfo eventInfo = obj.GetType().GetEvent(eventName);
  56.             Type eventDelegateType = eventInfo.EventHandlerType;
  57.  
  58.             DynamicMethod handler;
  59.             if (!s_emittedHandlers.TryGetValue(eventDelegateType, out handler))
  60.             {
  61.                 Type returnType;
  62.                 List<Type> parameterTypes;
  63.                 GetDelegateParameterAndReturnTypes(eventDelegateType,
  64.                     out parameterTypes, out returnType);
  65.  
  66.                 if (returnType != typeof(void))
  67.                     throw new NotSupportedException();
  68.  
  69.                 Type tcshType = tcsh.GetType();
  70.                 MethodInfo setResultMethodInfo = tcshType.GetMethod(
  71.                     "SetResult", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
  72.  
  73.                 // I'm going to create an instance-like method
  74.                 // so, first argument must an instance itself
  75.                 // i.e. TaskCompletionSourceHolder *this*
  76.                 parameterTypes.Insert(0, tcshType);
  77.                 Type[] parameterTypesAr = parameterTypes.ToArray();
  78.  
  79.                 handler = new DynamicMethod("unnamed",
  80.                     returnType, parameterTypesAr);
  81.  
  82.                 ILGenerator ilgen = handler.GetILGenerator();
  83.  
  84.                 // declare local variable of type object[]
  85.                 LocalBuilder arr = ilgen.DeclareLocal(typeof(object[]));
  86.                 // push array's size onto the stack
  87.                 ilgen.Emit(OpCodes.Ldc_I4, parameterTypesAr.Length - 1);
  88.                 // create an object array of the given size
  89.                 ilgen.Emit(OpCodes.Newarr, typeof(object));
  90.                 // and store it in the local variable
  91.                 ilgen.Emit(OpCodes.Stloc, arr);
  92.  
  93.                 // iterate thru all arguments except the zero one (i.e. *this*)
  94.                 // and store them to the array
  95.                 for (int i = 1; i < parameterTypesAr.Length; i++)
  96.                 {
  97.                     // push the array onto the stack
  98.                     ilgen.Emit(OpCodes.Ldloc, arr);
  99.                     // push the argument's index onto the stack
  100.                     ilgen.Emit(OpCodes.Ldc_I4, i - 1);
  101.                     // push the argument onto the stack
  102.                     ilgen.Emit(OpCodes.Ldarg, i);
  103.  
  104.                     // check if it is of a value type
  105.                     // and perform boxing if necessary
  106.                     if (parameterTypesAr[i].IsValueType)
  107.                         ilgen.Emit(OpCodes.Box, parameterTypesAr[i]);
  108.  
  109.                     // store the value to the argument's array
  110.                     ilgen.Emit(OpCodes.Stelem, typeof(object));
  111.                 }
  112.  
  113.                 // load zero-argument (i.e. *this*) onto the stack
  114.                 ilgen.Emit(OpCodes.Ldarg_0);
  115.                 // load the array onto the stack
  116.                 ilgen.Emit(OpCodes.Ldloc, arr);
  117.                 // call this.SetResult(arr);
  118.                 ilgen.Emit(OpCodes.Call, setResultMethodInfo);
  119.                 // and return
  120.                 ilgen.Emit(OpCodes.Ret);
  121.  
  122.                 s_emittedHandlers.Add(eventDelegateType, handler);
  123.             }
  124.  
  125.             Delegate dEmitted = handler.CreateDelegate(eventDelegateType, tcsh);
  126.             tcsh.Target = obj;
  127.             tcsh.EventInfo = eventInfo;
  128.             tcsh.Delegate = dEmitted;
  129.  
  130.             eventInfo.AddEventHandler(obj, dEmitted);
  131.             return tcs.Task;
  132.         }
  133.     }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement