首页 >后端开发 >C++ >如何使用通用 FromEvent 方法等待任何类型的任何事件?

如何使用通用 FromEvent 方法等待任何类型的任何事件?

Mary-Kate Olsen
Mary-Kate Olsen原创
2024-12-31 22:38:19929浏览

How Can I Await Any Event on Any Type Using a General-Purpose FromEvent Method?

通用 FromEvent 方法

原始的 FromEvent 方法需要为每个要等待的事件和类创建单独的方法。为了消除样板代码,开发人员寻求一种更通用的解决方案。

一种方法涉及使用反射来检索事件及其委托类型。然而,访问委托的参数和修改 TaskCompletionSource 带来了挑战。

自定义解决方案

这是一个利用 IL 发射和事件处理程序的自定义解决方案:

public static class ExtensionMethods
{
    public static Task<object[]> FromEvent<T>(this T obj, string eventName)
    {
        // Create a TaskCompletionSourceHolder to manage the TaskCompletionSource and event handler
        var tcsh = new TaskCompletionSourceHolder();

        // Get the event and its delegate type
        EventInfo eventInfo = obj.GetType().GetEvent(eventName);
        Type eventDelegateType = eventInfo.EventHandlerType;

        // Create a dynamic method to handle the event
        DynamicMethod handler = CreateEventHandler(eventDelegateType);

        // Add the event handler to the target object
        eventInfo.AddEventHandler(obj, handler.CreateDelegate(eventDelegateType, tcsh));

        // Return the Task from the TaskCompletionSourceHolder
        return tcsh.Task;
    }

    private static DynamicMethod CreateEventHandler(Type eventDelegateType)
    {
        // Get the parameter types of the event delegate
        var parameterTypes = GetDelegateParameterTypes(eventDelegateType);

        // Insert the TaskCompletionSourceHolder as the first parameter
        parameterTypes.Insert(0, typeof(TaskCompletionSourceHolder));

        // Create a new dynamic method and IL generator
        DynamicMethod handler = new DynamicMethod("EventHandler", typeof(void), parameterTypes, true);
        ILGenerator ilgen = handler.GetILGenerator();

        // Load the TaskCompletionSourceHolder and create an array to store the arguments
        ilgen.Emit(OpCodes.Ldarg_0);
        ilgen.Emit(OpCodes.Ldc_I4, parameterTypes.Count - 1);
        ilgen.Emit(OpCodes.Newarr, typeof(object));

        // Store each argument in the array
        for (int i = 1; i < parameterTypes.Count; i++)
        {
            ilgen.Emit(OpCodes.Ldloc_0);
            ilgen.Emit(OpCodes.Ldc_I4, i - 1);
            ilgen.Emit(OpCodes.Ldarg, i);
            ilgen.Emit(OpCodes.Stelem_Ref);
        }

        // Call the SetResult method on the TaskCompletionSourceHolder
        ilgen.Emit(OpCodes.Call, typeof(TaskCompletionSourceHolder).GetMethod("SetResult"));

        // Return from the method
        ilgen.Emit(OpCodes.Ret);

        return handler;
    }

    private static List<Type> GetDelegateParameterTypes(Type delegateType)
    {
        var parameters = delegateType.GetMethod("Invoke").GetParameters();
        var parameterTypes = parameters.Select(p => p.ParameterType).ToList();
        return parameterTypes;
    }
}

通过此方法,您现在可以等待任何事件类型:

await new MyClass().FromEvent("MyEvent");

此解决方案的优点

  • 支持具有任意数量或类型参数的任何事件类型。
  • 不需要将任务转换为特定的返回类型。
  • 无需创建多个不同类型和事件的 FromEvent 方法。

以上是如何使用通用 FromEvent 方法等待任何类型的任何事件?的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn