有没有办法简化C#中的反映代理代码? [英] Is there a way to simplify this reflective delegate code in C#?

查看:116
本文介绍了有没有办法简化C#中的反映代理代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In this response, I came up with the following helper method that could be reused by different Task creators to convert events into task completion sources.

// 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<?,T> ,其中 Func 将采用事件并将其转换为任何 tcs.SetResult take。

In each of the three examples I gave that use the helper method, there's a tcs.SetResult component, which makes me think there's a way to move that to the helper method too, which might perhaps simplify the signatures, so that perhaps the helper method would just have to accept a Func<?, T>, where that Func would take the output of the event and convert it to whatever tcs.SetResult takes.

即,我认为必须有一种方法来创建一个帮手,所以我可以把它写成

i.e., I'm thinking there must be a way to create a helper so I can write it as

// 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<?,T> 以上。这个例子需要成为两个参数。可以以对象的方式传递吗?我有一种感觉可能是可能的,但如果是这样,它需要一些真正的反思魔法。

, but that's why I don't know the ? in Func<?, T> above. This one for example needs ? to be two parameters. Could it be passed in as object somehow? I have a feeling it could be possible, but if so it needs some real reflection magic.

推荐答案

你可以使用表达式

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

这篇关于有没有办法简化C#中的反映代理代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆