通用FromEvent方法

通用FromEvent方法,第1张

通用FromEvent方法

干得好:

internal class TaskCompletionSourceHolder{    private readonly TaskCompletionSource<object[]> m_tcs;    internal object Target { get; set; }    internal EventInfo EventInfo { get; set; }    internal Delegate Delegate { get; set; }    internal TaskCompletionSourceHolder(TaskCompletionSource<object[]> tsc)    {        m_tcs = tsc;    }    private void SetResult(params object[] args)    {        // this method will be called from emitted IL        // so we can set result here, unsubscribe from the event        // or do whatever we want.        // object[] args will contain arguments        // passed to the event handler        m_tcs.SetResult(args);        EventInfo.RemoveEventHandler(Target, Delegate);    }}public static class ExtensionMethods{    private static Dictionary<Type, DynamicMethod> s_emittedHandlers =        new Dictionary<Type, DynamicMethod>();    private static void GetDelegateParameterAndReturnTypes(Type delegateType,        out List<Type> parameterTypes, out Type returnType)    {        if (delegateType.baseType != typeof(MulticastDelegate)) throw new ArgumentException("delegateType is not a delegate");        MethodInfo invoke = delegateType.GetMethod("Invoke");        if (invoke == null) throw new ArgumentException("delegateType is not a delegate.");        ParameterInfo[] parameters = invoke.GetParameters();        parameterTypes = new List<Type>(parameters.Length);        for (int i = 0; i < parameters.Length; i++) parameterTypes.Add(parameters[i].ParameterType);        returnType = invoke.ReturnType;    }    public static Task<object[]> FromEvent<T>(this T obj, string eventName)    {        var tcs = new TaskCompletionSource<object[]>();        var tcsh = new TaskCompletionSourceHolder(tcs);        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(); Type tcshType = tcsh.GetType(); MethodInfo setResultMethodInfo = tcshType.GetMethod(     "SetResult", BindingFlags.NonPublic | BindingFlags.Instance); // I'm going to create an instance-like method // so, first argument must an instance itself // i.e. TaskCompletionSourceHolder *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's size onto the stack  ilgen.Emit(OpCodes.Ldc_I4, parameterTypesAr.Length - 1); // create an object array of the given size 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*) // and store them to the array for (int i = 1; i < parameterTypesAr.Length; i++) {     // push the array onto the stack     ilgen.Emit(OpCodes.Ldloc, arr);     // push the argument's index onto the stack     ilgen.Emit(OpCodes.Ldc_I4, i - 1);     // push the argument onto the stack     ilgen.Emit(OpCodes.Ldarg, i);     // check if it is of a value type     // and perform boxing if necessary     if (parameterTypesAr[i].IsValueType)         ilgen.Emit(OpCodes.Box, parameterTypesAr[i]);     // store the value to the argument's array     ilgen.Emit(OpCodes.Stelem, typeof(object)); } // load zero-argument (i.e. *this*) onto the stack ilgen.Emit(OpCodes.Ldarg_0); // load the 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;    }}

这段代码适用于几乎所有返回void的事件(无论参数列表如何)。

如有必要,可以对其进行改进以支持任何返回值。

您可以在下面看到Dax方法和mine方法之间的区别:

static async void Run() {    object[] result = await new MyClass().FromEvent("Fired");    Console.WriteLine(string.Join(", ", result.Select(arg =>        arg.ToString()).ToArray())); // 123, abcd}public class MyClass {    public delegate void TwoThings(int x, string y);    public MyClass() {        new Thread(() => {     Thread.Sleep(1000);     Fired(123, "abcd"); }).Start();    }    public event TwoThings Fired;}

简而言之,我的代码 实际上 支持任何类型的委托类型。您不应该(也不需要)像那样明确指定它

TaskFromEvent<int, string>



欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5567420.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-14
下一篇 2022-12-14

发表评论

登录后才能评论

评论列表(0条)

保存