基于字符串类型通过反射调用泛型异步方法填充列表的问题
反射调用异步泛型方法的正确实现及性能分析
一、正确实现方式
你遇到的报错核心原因是:method.Invoke返回的是object类型,实际对应Task<IEnumerable<T>>,但编译器无法识别它是可await的Task,也不能直接将Task<IEnumerable<T>>赋值给IList。正确的处理步骤如下:
1. 调用方法并await Task
先将Invoke的结果转换为非泛型Task,等待任务完成后再提取结果:
// 调用泛型方法,返回Task对象 var task = (Task)method.Invoke(_sql, args)!; // 等待异步任务完成 await task;
2. 提取任务结果并转换为集合
通过反射获取Task的Result属性,得到IEnumerable<T>实例,再将其元素添加到你创建的IList中:
// 获取Task的Result属性值(即IEnumerable<T>) var resultProperty = task.GetType().GetProperty("Result")!; IEnumerable resultEnumerable = (IEnumerable)resultProperty.GetValue(task)!; // 将结果元素添加到IList中 foreach (var item in resultEnumerable) { l.Add(item); }
或者更直接的方式:如果不需要提前创建IList,可以直接将结果转换为IList(因为List<T>实现了IList接口):
var task = (Task)method.Invoke(_sql, args)!; await task; var result = task.GetType().GetProperty("Result")!.GetValue(task); // 直接将IEnumerable<T>转为IList l = (IList)result!;
二、反射调用的性能与高并发影响
1. 性能开销点
反射的性能损耗主要集中在两个环节:
- 方法查找与筛选:
GetMethods()+Where()+First()的组合会遍历类型的所有方法,耗时较高。 - 泛型方法实例化:
MakeGenericMethod()会动态生成泛型方法的具体实例,这一操作会产生额外的元数据和内存开销。
相比之下,Invoke本身的性能开销反而较小。
2. 高并发场景的影响
如果不做优化,在高并发场景下重复执行上述反射操作,会导致:
- 大量的CPU时间消耗在方法查找和泛型实例化上。
- 频繁的动态类型生成会增加GC压力,可能引发频繁垃圾回收。
3. 优化方案
最有效的优化方式是缓存泛型方法的MethodInfo实例,避免重复执行反射查找和实例化:
// 定义静态缓存字典,按类型缓存对应的MethodInfo private static readonly System.Collections.Concurrent.ConcurrentDictionary<Type, MethodInfo> _queryAsyncMethodCache = new(); // 获取方法时优先从缓存读取 if (!_queryAsyncMethodCache.TryGetValue(type, out var cachedMethod)) { cachedMethod = _sql.GetType().GetMethods() .Where(c => c.Name == "QueryAsync" && c.GetParameters()[1].ParameterType == typeof(DynamicParameters)) .First() .MakeGenericMethod(type); _queryAsyncMethodCache.TryAdd(type, cachedMethod); } // 使用缓存的方法实例 var method = cachedMethod;
使用ConcurrentDictionary可以避免高并发场景下的锁竞争问题,进一步提升性能。
内容的提问来源于stack exchange,提问作者Dorian
相关产品推荐
相关产品推荐

