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

如何用C#表达式树编译动态属性拼接的字符串插值Lambda?

动态生成属性值拼接的Lambda表达式(基于配置文件)

当然可以用表达式树动态编译出这类Lambda,而且这是性能最优的方案之一,另外也可以用反射实现(适合低性能要求场景)。下面分别给出具体实现:

一、表达式树方案

通过构建表达式树并编译,能得到和手写Lambda几乎一致性能的委托,步骤如下:

  1. 解析配置文件得到要拼接的属性名列表
  2. 构建参数表达式(对应Lambda中的x)
  3. 为每个属性名生成属性访问表达式
  4. 调用string.Join拼接所有属性值
  5. 编译表达式树为Func<T, string>

完整代码实现:

using System.Linq.Expressions;
using System.Reflection;

public static class KeyFactoryBuilder
{
    public static Func<T, string> Create<T>(List<string> propertyNames)
    {
        // 定义Lambda的输入参数:x
        var parameter = Expression.Parameter(typeof(T), "x");

        // 生成每个属性的访问表达式
        var propertyAccessors = propertyNames
            .Select(name => typeof(T).GetProperty(name) ?? 
                throw new ArgumentException($"属性 {name} 在类型 {typeof(T).Name} 中不存在"))
            .Select(property => Expression.Property(parameter, property))
            .Cast<Expression>()
            // 如果属性不是string类型,需要添加ToString()调用:
            // .Select(expr => Expression.Call(expr, "ToString", Type.EmptyTypes))
            .ToList();

        // 构建string.Join的调用表达式
        var joinMethod = typeof(string).GetMethod(
            "Join", 
            new[] { typeof(string), typeof(string[]) }
        ) ?? throw new InvalidOperationException("未找到string.Join方法");
        
        var arrayExpr = Expression.NewArrayInit(typeof(string), propertyAccessors);
        var joinCall = Expression.Call(joinMethod, Expression.Constant("|"), arrayExpr);

        // 编译为委托
        return Expression.Lambda<Func<T, string>>(joinCall, parameter).Compile();
    }
}

使用示例

// 从JSON配置读取的属性列表
var targetProperties = new List<string> { "PropertyOne", "PropertyTwo" };
var keyFactory = KeyFactoryBuilder.Create<SomeClass>(targetProperties);

var someClass = new SomeClass
{
    PropertyOne = "Value1",
    PropertyTwo = "Value2",
    PropertyThree = "Value3",
    PropertyFour = "Value4",
};

string resultKey = keyFactory(someClass); // 输出:"Value1|Value2"

二、反射方案(简单但性能稍逊)

如果对性能要求不高,反射实现更简洁,直接在委托中通过反射获取属性值并拼接:

public static Func<T, string> CreateUsingReflection<T>(List<string> propertyNames)
{
    var properties = propertyNames
        .Select(name => typeof(T).GetProperty(name) ?? 
            throw new ArgumentException($"属性 {name} 在类型 {typeof(T).Name} 中不存在"))
        .ToList();

    return instance => string.Join("|", properties.Select(p => p.GetValue(instance) as string));
}

方案对比

  • 表达式树:编译后委托的执行性能和手写Lambda基本一致,适合高频调用场景,编译过程只需要执行一次。
  • 反射:实现简单,但每次调用都要通过反射获取属性值,性能比表达式树差,适合调用频率低的场景。

内容的提问来源于stack exchange,提问作者Anton

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 01:30:55