如何移除方法名判断,实现基于Expression调用IEnumerable<double>聚合方法
问题描述
我尝试创建一个利用Expression调用IEnumerable<double>的Min、Sum等聚合方法的函数,但Sum和Average方法无法正常工作。当前代码通过方法名判断来区分处理,代码如下:
Func<IEnumerable<double>, double> BuildFunc(string func) { var source = Expression.Parameter(typeof(IEnumerable<double>), "source"); //For now added condition to handle it. var methodCallExpression = func == "Sum" || func == "Average" ? Expression.Call(typeof(Enumerable), func, Array.Empty<Type>(), source) : Expression.Call(typeof(Enumerable), func, new Type[] { typeof(double) }, source); Expression conversion = Expression.Convert(methodCallExpression, typeof(double)); var methodCall = Expression.Lambda<Func<IEnumerable<double>, double>>( conversion, source); return methodCall.Compile(); }
调用示例:
IEnumerable<double> myList = new List<double> { 10, 20 }; var func = BuildFunc("Min"); double result = func(myList); var func = BuildFunc("Sum"); double result = func(myList);
请问如何修改该函数,移除方法名判断逻辑,实现通用的聚合方法调用?
解决方案
核心问题在于Enumerable类中不同聚合方法的重载形式不同(部分是泛型方法,部分是非泛型的针对IEnumerable<double>的重载),硬编码方法名判断不够通用。我们可以通过反射自动匹配正确的方法重载,彻底移除方法名判断逻辑:
Func<IEnumerable<double>, double> BuildFunc(string func) { var sourceType = typeof(IEnumerable<double>); // 反射查找Enumerable中符合要求的方法:方法名匹配、单参数为IEnumerable<double>、返回值可转换为double var targetMethod = typeof(Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static) .Where(m => m.Name == func) .Select(m => new { Method = m, Params = m.GetParameters() }) .Where(x => x.Params.Length == 1 && x.Params[0].ParameterType == sourceType) .Select(x => x.Method) .FirstOrDefault(); if (targetMethod == null) throw new ArgumentException($"找不到针对IEnumerable<double>的有效聚合方法:{func}"); var sourceParam = Expression.Parameter(sourceType, "source"); var methodCallExpr = Expression.Call(targetMethod, sourceParam); // 仅当方法返回值不是double时才执行转换(针对少数特殊情况) var finalExpr = targetMethod.ReturnType == typeof(double) ? methodCallExpr : Expression.Convert(methodCallExpr, typeof(double)); return Expression.Lambda<Func<IEnumerable<double>, double>>(finalExpr, sourceParam).Compile(); }
代码说明
- 反射匹配重载:遍历
Enumerable的静态公共方法,筛选出满足以下条件的方法:- 方法名与传入的
func一致 - 仅包含一个参数,且参数类型为
IEnumerable<double>
- 方法名与传入的
- 动态构建表达式:用找到的目标方法直接构建方法调用表达式,无需手动区分泛型/非泛型重载
- 安全类型转换:自动判断方法返回值是否为
double,仅在必要时执行类型转换,保证兼容性
验证调用
IEnumerable<double> myList = new List<double> { 10, 20 }; var minFunc = BuildFunc("Min"); Console.WriteLine(minFunc(myList)); // 输出:10 var sumFunc = BuildFunc("Sum"); Console.WriteLine(sumFunc(myList)); // 输出:30 var avgFunc = BuildFunc("Average"); Console.WriteLine(avgFunc(myList)); // 输出:15 var maxFunc = BuildFunc("Max"); Console.WriteLine(maxFunc(myList)); // 输出:20
这种实现方式支持所有Enumerable中针对IEnumerable<double>的聚合方法(只要返回值可转换为double),无需修改代码即可扩展支持更多方法。
内容的提问来源于stack exchange,提问作者Ankush Madankar
相关产品推荐
相关产品推荐

