3

この応答で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)))));
}

ヘルパーメソッドを使用する3つの例のそれぞれに、tcs.SetResultコンポーネントがあります。これにより、それをヘルパーメソッドに移動する方法もあると思います。これにより、署名が単純化される可能性があります。を受け入れるためFunc<?, T>に、それ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<?, T>。たとえば、これは 2つのパラメータである必要?があります。どういうわけか受け継がれるのでしょうか?それは可能だと思いますが、もしそうなら、本当の反射魔法が必要です。object

4

1 に答える 1

3

あなたが使用することができます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;
}
于 2012-10-14T20:31:01.393 に答える