如何用C#表达式树编译动态属性拼接的字符串插值Lambda?
动态生成属性值拼接的Lambda表达式(基于配置文件)
当然可以用表达式树动态编译出这类Lambda,而且这是性能最优的方案之一,另外也可以用反射实现(适合低性能要求场景)。下面分别给出具体实现:
一、表达式树方案
通过构建表达式树并编译,能得到和手写Lambda几乎一致性能的委托,步骤如下:
- 解析配置文件得到要拼接的属性名列表
- 构建参数表达式(对应Lambda中的
x) - 为每个属性名生成属性访问表达式
- 调用
string.Join拼接所有属性值 - 编译表达式树为
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
相关产品推荐
相关产品推荐

