【问题标题】:Is there a way to simplify this reflective delegate code in C#?有没有办法在 C# 中简化这种反射委托代码?
【发布时间】:2012-10-14 18:55:55
【问题描述】:

this 响应中,我提出了以下帮助方法,不同的Task 创建者可以重用它来将事件转换为任务完成源。

// Helper method
static Task<T> TaskFromEventHelper<T>(object target, string eventName, Func<TaskCompletionSource<T>, object> resultSetterFactory) {
    var tcs = new TaskCompletionSource<T>();
    var addMethod = target.GetType().GetEvent(eventName).GetAddMethod();
    var delegateType = addMethod.GetParameters()[0].ParameterType;
    var d = Delegate.CreateDelegate(delegateType, resultSetterFactory(tcs), "Invoke");
    addMethod.Invoke(target, new object[] {d});
    return tcs.Task;
}

// Empty events (Action style)
static Task TaskFromEvent(object target, string eventName) {
    return TaskFromEventHelper(target, eventName, (Func<TaskCompletionSource<object>, object>)(tcs => (Action)(() => tcs.SetResult(null))));
}

// One-value events (Action<T> style)
static Task<T> TaskFromEvent<T>(object target, string eventName) {
    return TaskFromEventHelper(target, eventName, (Func<TaskCompletionSource<T>, object>)(tcs => (Action<T>)(tcs.SetResult)));
}

// Two-value events (Action<T1, T2> or EventHandler style)
static Task<Tuple<T1, T2>> TaskFromEvent<T1, T2>(object target, string eventName) {
    return TaskFromEventHelper(target, eventName, (Func<TaskCompletionSource<Tuple<T1, T2>>, object>)(tcs => (Action<T1, T2>)((t1, t2) => tcs.SetResult(Tuple.Create(t1, t2)))));
}

在我给出的使用辅助方法的三个示例中,都有一个 tcs.SetResult 组件,这让我认为也有一种方法可以将它移到辅助方法中,这可能会简化签名,所以也许辅助方法只需要接受Func&lt;?, T&gt;,其中Func 将获取event 的输出并将其转换为tcs.SetResult 的输出。

也就是说,我认为必须有一种方法可以创建一个助手,这样我就可以将它写成

// Empty events (Action style)
static Task TaskFromEvent(object target, string eventName) {
    return TaskFromEventHelper<object>(target, eventName, new Func<object>(() => null));
}

// One-value events (Action<T> style)
static Task<T> TaskFromEvent<T>(object target, string eventName) {
    return TaskFromEventHelper<T>(target, eventName, new Func<T, T>(t => t));
}

// Two-value events (Action<T1, T2> or EventHandler style)
static Task<Tuple<T1, T2>> TaskFromEvent<T1, T2>(object target, string eventName) {
    return TaskFromEventHelper<Tuple<T1, T2>>(target, eventName, new Func<T1, T2, Tuple<T1, T2>>(Tuple.Create));
}

,但这就是为什么我不知道上面Func&lt;?, T&gt; 中的?。这一个例如需要?两个 参数。它可以以某种方式作为object 传递吗?我觉得这是可能的,但如果是这样的话,它需要一些真正的反射魔法。

【问题讨论】:

    标签: c# reflection delegates


    【解决方案1】:

    你可以使用Expression:

    static Task<T> TaskFromEventHelper<T>(object target, string eventName, Delegate resultSetter)
    {
        var tcs             = new TaskCompletionSource<T>();
        var addMethod       = target.GetType().GetEvent(eventName).GetAddMethod();
        var delegateType    = addMethod.GetParameters()[0].ParameterType;
        var methodInfo      = delegateType.GetMethod("Invoke");
        var parameters      = methodInfo.GetParameters()
                                        .Select(a => Expression.Parameter(a.ParameterType))
                                        .ToArray();
    
        // building method, which argument count and
        // their types are not known at compile time
        var exp = // (%arguments%) => tcs.SetResult(resultSetter.Invoke(%arguments%))
            Expression.Lambda(
                delegateType,
                Expression.Call(
                    Expression.Constant(tcs),
                    tcs.GetType().GetMethod("SetResult"),
                    Expression.Call(
                        Expression.Constant(resultSetter),
                        resultSetter.GetType().GetMethod("Invoke"),
                        parameters)),
                parameters);
    
        addMethod.Invoke(target, new object[] { exp.Compile() });
        return tcs.Task;
    }
    

    【讨论】:

    • 厉害,确实简化了调用方法!
    猜你喜欢
    • 2019-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多