异步委托 casting/conversion

Async delegates casting/conversion

我有一些 class 和 属性:

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

TResult 类型可以是任何类型:intdecimalTaskTask<>.

假设 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 不能在这里使用,因为 return 这个委托的类型应该是 Task-like 或 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));
    };
}

此方法也无法编译,因为延续结果不是 TResult,而是 Task<TResult>

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

另外请记住,异步时不允许阻塞,因此仅在任务上使用 .Result.Wait() 并非如此。

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

谢谢

更新

我试图等待 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;
    };
}

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

您的问题似乎是让编译器相信您确定 TResultTask/Task<T>.

如果您使用扩展方法,您可以编写一个 MyMethod,使其仅适用于 Test 个对象,其中 TResultTask,另一个重载仅适用于 Test 个对象,其中 TResultTask<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);
        };
    }
}