泛型类TResult为Task类型时Async委托转换与强制转型问题
可行实现方案
方案1:通过dynamic绕过编译期类型校验
该方案实现最简单,利用dynamic在运行时解析类型,无阻塞调用,也不需要修改原有泛型类结构:
public void MyMethod(Func<TInput, Task<bool>> predicate, Func<TInput, TResult> ifTrue) { // 运行时校验TResult类型合法性,可选 if (!typeof(Task).IsAssignableFrom(typeof(TResult))) throw new InvalidOperationException("TResult必须为Task或Task<>类型"); this.MyProperty = inputDelegate => async input => { var predicateResult = await predicate.Invoke(input); // 用dynamic绕开编译期对async返回值的类型校验 dynamic resultTask = predicateResult ? await (dynamic)ifTrue.Invoke(input) : await (dynamic)inputDelegate.Invoke(input); return resultTask; }; }
原理:async方法返回Task/Task
方案2:运行时生成适配委托(无dynamic开销)
如果不想引入dynamic的运行时解析开销,可以在类初始化时通过表达式树生成对应TResult类型的适配逻辑,缓存后直接调用,性能和手写原生代码一致:
// 缓存生成好的适配委托,仅初始化时生成一次 private Func<Func<TInput, Task<bool>>, Func<TInput, TResult>, Func<TInput, TResult>, Func<TInput, TResult>> _cachedAdapter; public Test() { if (typeof(Task).IsAssignableFrom(typeof(TResult))) { _cachedAdapter = BuildTaskAdapter(); } } public void MyMethod(Func<TInput, Task<bool>> predicate, Func<TInput, TResult> ifTrue) { if (_cachedAdapter == null) throw new InvalidOperationException("TResult必须为Task或Task<>类型"); this.MyProperty = inputDelegate => _cachedAdapter(predicate, ifTrue, inputDelegate); } private Func<Func<TInput, Task<bool>>, Func<TInput, TResult>, Func<TInput, TResult>, Func<TInput, TResult>> BuildTaskAdapter() { // 表达式树参数定义 var predicateParam = Expression.Parameter(typeof(Func<TInput, Task<bool>>), "predicate"); var ifTrueParam = Expression.Parameter(typeof(Func<TInput, TResult>), "ifTrue"); var inputDelegateParam = Expression.Parameter(typeof(Func<TInput, TResult>), "inputDelegate"); var inputParam = Expression.Parameter(typeof(TInput), "input"); // 拼接核心逻辑:调用predicate,根据返回结果选择执行ifTrue/inputDelegate,返回对应Task // 非泛型Task和泛型Task<T>分别处理,生成对应类型的返回值 Expression body; if (typeof(TResult) == typeof(Task)) { // 非泛型Task适配逻辑 var predicateCall = Expression.Invoke(predicateParam, inputParam); var awaitPredicate = Expression.Await(predicateCall); var condition = Expression.Condition( awaitPredicate, Expression.Invoke(ifTrueParam, inputParam), Expression.Invoke(inputDelegateParam, inputParam) ); body = Expression.Lambda<Func<TInput, TResult>>(condition, inputParam); } else { // 泛型Task<T>适配逻辑,拿到泛型参数后生成对应await逻辑 var genericArg = typeof(TResult).GetGenericArguments()[0]; var predicateCall = Expression.Invoke(predicateParam, inputParam); var awaitPredicate = Expression.Await(predicateCall); var trueCall = Expression.Invoke(ifTrueParam, inputParam); var falseCall = Expression.Invoke(inputDelegateParam, inputParam); var condition = Expression.Condition( awaitPredicate, Expression.Await(Expression.Convert(trueCall, typeof(Task<>).MakeGenericType(genericArg))), Expression.Await(Expression.Convert(falseCall, typeof(Task<>).MakeGenericType(genericArg))) ); body = Expression.Lambda<Func<TInput, TResult>>( Expression.Convert(Expression.TaskFromResult(condition), typeof(TResult)), inputParam ); } return Expression.Lambda<Func<Func<TInput, Task<bool>>, Func<TInput, TResult>, Func<TInput, TResult>, Func<TInput, TResult>>>( body, predicateParam, ifTrueParam, inputDelegateParam ).Compile(); }
注意事项
- 两种方案都不存在
.Result/.Wait()等阻塞调用,无死锁风险 - 完全保留原有泛型类
Test<TInput, TResult>的定义,不需要修改MyProperty的类型 - 运行时类型校验确保仅当TResult为Task兼容类型时逻辑可用,避免非预期错误
内容的提问来源于stack exchange,提问作者Dixy
相关产品推荐
相关产品推荐

