首页 > 解决方案 > 异步委托转换/转换

问题描述

我有一些财产类:

public class Test<TInput, TResult>
{
    public Func<Func<TInput, TResult>, Func<TInput, TResult>> MyProperty { get; set; }
}

TResult类型可以是任何类型:int, decimal, Task, Task<>.

假设TResultTaskTask<>(仅用于此示例)。请不要建议转换为Func<TInput, Task<TResult>>- 这对我来说是不可能的。

现在我有一些方法(不编译,见下文):

public void MyMethod(Func<TInput, Task<bool>> predicate, Func<TInput, TResult> ifTrue)
{
    this.MyProperty = inputDelegate => async input =>
    {
        var predicateResult = await predicate.Invoke(input);

        return predicateResult ? await ifTrue.Invoke(input) : await inputDelegate.Invoke(input);
    };
}

显然有编译时错误说async不能在这里使用,因为这个委托的返回类型应该是Task-like or void(我知道TResultTask或者我可以检查,但编译器不知道)。

我也尝试继续:

public void MyMethod2(Func<TInput, Task<bool>> predicate, Func<TInput, TResult> ifTrue)
{
    this.MyProperty = inputDelegate => input =>
    {
        return predicate.Invoke(input)
            .ContinueWith(t => t.Result ? ifTrue.Invoke(input) : inputDelegate.Invoke(input));
    };
}

此方法也不会编译为 continuation 结果 is not TResult, but Task<TResult>

我想应该有办法做到这一点(我正在玩铸造和委托转换,但我无法让它工作)。

还要记住,它不允许阻塞异步,所以不能只使用.Resulton.Wait()任务。

先前关于TResultis Task-like 类型的假设是否有可能以某种方式处理这种情况?

谢谢

更新

我试图等待内部继续predicate

public void MyMethod3(Func<TInput, Task<bool>> predicate, Func<TInput, TResult> ifTrue)
{
    this.MyProperty = inputDelegate => input =>
    {
        var continuationTask = predicate.Invoke(input)
            .ContinueWith
            (
                async t =>
                {
                    var task = (Task)(object)(t.Result ? ifTrue.Invoke(input) : inputDelegate.Invoke(input));

                    await task;

                    object result = null;

                    var taskType = task.GetType();

                    if (taskType.IsGenericType) // Task<>
                    {
                        // task is already completed because of await above
                        result = taskType.GetProperty(nameof(Task<object>.Result)).GetValue(task);
                    }

                    return result;
                }
            ).Unwrap(); // make return type Task<object> from Task<Task<object>>

        // InvalidCastException -> if TResult is something different form Task<object>
        // if TResult is Task<int> then cast fails (because Task<int> and Task<object> are different types and task is not co/contra -variant)
        return (TResult)(object)continuationTask;
    };
}

但我不能将最终结果转换为所需的类型。

标签: c#.net.net-coredelegatestask

解决方案


似乎您的问题是让编译器相信您确定是/ 。TResultTaskTask<T>

如果您使用扩展方法,您可以编写一个MyMethod仅适用于Test对象 where TResultis 的Task重载,以及另一个仅适用于Test对象 where TResultis 的重载Task<T>

public static class TestExtensions {
    public static void MyMethod<TInput>(this Test<TInput, Task> test, Func<TInput, Task<bool>> predicate, Func<TInput, Task> ifTrue)
    {
        test.MyProperty = inputDelegate => async input =>
        {
            var predicateResult = await predicate.Invoke(input);

            if (predicateResult) {
                await ifTrue(input);
            } else {
                await inputDelegate(input);
            }
        };
    }
    public static void MyMethod<TInput, TResult>(this Test<TInput, Task<TResult>> test, Func<TInput, Task<bool>> predicate, Func<TInput, Task<TResult>> ifTrue)
    {
        test.MyProperty = inputDelegate => async input =>
        {
            var predicateResult = await predicate.Invoke(input);
            return predicateResult ? await ifTrue(input) : await inputDelegate(input);
        };
    }
}

推荐阅读