C#跨程序集dynamic await非公开类型泛型Task/ValueTask报错如何解决
问题结论
该问题是C# dynamic的固有运行机制导致的,不属于业务逻辑编写错误。
底层运行逻辑
dynamic的运行时绑定由C#运行时绑定器实现,绑定过程会严格检查涉及类型对当前执行代码所在程序集的可见性。当TaskHelper迁移到独立程序集后,测试项目中的internal类InternalClass对TaskHelper所在程序集不可见,导致绑定器无法正确解析泛型等待类型的成员:
- 针对
Task<InternalClass>场景:绑定器无法识别泛型参数类型,错误将await Task<InternalClass>的返回值判定为void,赋值给object变量时触发转换异常。 - 针对
ValueTask<InternalClass>场景:绑定器无法解析ValueTask<T>的GetAwaiter方法,回退到其父类System.ValueType上查找对应成员,查找失败抛出异常。
当类型处于同一程序集或者将InternalClass改为public时,泛型参数类型对TaskHelper所在程序集可见,绑定器可以正常完成成员解析,因此运行正常。
修复方案
完全弃用dynamic实现,改用反射构造泛型调用的方式处理等待逻辑,避开dynamic绑定的可见性检查限制。修改后的代码如下:
public static class TaskHelper { private static readonly Type TypeOfVoidTaskResult = Type.GetType("System.Threading.Tasks.VoidTaskResult"); private static readonly Type TypeOfTaskOfVoidTaskResult = typeof(Task<>).MakeGenericType(TypeOfVoidTaskResult); private static readonly MethodInfo AwaitTaskMethod = typeof(TaskHelper).GetMethod(nameof(AwaitTaskCore), BindingFlags.NonPublic | BindingFlags.Static); private static readonly MethodInfo AwaitValueTaskMethod = typeof(TaskHelper).GetMethod(nameof(AwaitValueTaskCore), BindingFlags.NonPublic | BindingFlags.Static); public static async Task<object> AwaitObject(object obj) { if (obj == null) return null; var type = obj.GetType(); // 处理非泛型Task、非泛型ValueTask、返回VoidTaskResult的泛型Task if (type == typeof(Task) || type == typeof(ValueTask) || type == TypeOfTaskOfVoidTaskResult) { if (obj is Task task) await task; else if (obj is ValueTask valueTask) await valueTask; return null; } // 处理泛型Task<> if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>)) { var genericArg = type.GetGenericArguments()[0]; var typedMethod = AwaitTaskMethod.MakeGenericMethod(genericArg); var awaiterTask = (Task<object>)typedMethod.Invoke(null, new[] { obj }); return await awaiterTask; } // 处理泛型ValueTask<> if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ValueTask<>)) { var genericArg = type.GetGenericArguments()[0]; var typedMethod = AwaitValueTaskMethod.MakeGenericMethod(genericArg); var awaiterTask = (Task<object>)typedMethod.Invoke(null, new[] { obj }); return await awaiterTask; } return obj; } private static async Task<object> AwaitTaskCore<T>(Task<T> task) { return await task; } private static async Task<object> AwaitValueTaskCore<T>(ValueTask<T> valueTask) { return await valueTask; } }
该实现通过预定义的泛型核心处理方法,运行时根据等待对象的泛型参数类型构造对应的封闭泛型方法,通过反射调用完成等待逻辑,全程不涉及dynamic绑定,因此不受类型可见性限制,可以正常处理internal等非公开类型的泛型等待对象。如果对性能有要求,可以对构造后的泛型方法做字典缓存,避免每次反射构造的开销。
内容的提问来源于stack exchange,提问作者huoshan12345
相关产品推荐
相关产品推荐

