You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何移除方法名判断,实现基于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();
}

代码说明

  1. 反射匹配重载:遍历Enumerable的静态公共方法,筛选出满足以下条件的方法:
    • 方法名与传入的func一致
    • 仅包含一个参数,且参数类型为IEnumerable<double>
  2. 动态构建表达式:用找到的目标方法直接构建方法调用表达式,无需手动区分泛型/非泛型重载
  3. 安全类型转换:自动判断方法返回值是否为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 10:55:15