如何基于属性名列表动态生成Expression<Func<T, object>>表达式
动态生成匿名类型选择器表达式的实现方案
我需要针对给定类型T和属性名列表(比如{"prop1", "prop2"}),生成格式为x => new {x.prop1, x.prop2}的Expression<Func<T, object>>表达式。目前仅能创建单个属性的访问器,但不确定如何处理这种动态生成的匿名类型,现有单个属性的实现代码如下:
private Func<T, int> BuildChildSelectorLambda(string idPropertyName) { ParameterExpression argParam = Expression.Parameter(typeof(T), "x"); Expression selector = Expression.Property(argParam, typeof(T).GetProperty(idPropertyName)); return Expression.Lambda<Func<T,int>>(selector, argParam).Compile(); //TODO: CACHE EXPRESSIONS }
实现思路与代码
要生成动态匿名类型的选择器表达式,核心是动态构建对应属性的匿名类型,并生成初始化表达式。以下是完整实现:
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; public static class ExpressionBuilder { // 缓存已生成的动态类型,避免重复创建 private static readonly Dictionary<string, Type> _anonymousTypeCache = new Dictionary<string, Type>(); private static readonly object _cacheLock = new object(); public static Expression<Func<T, object>> BuildAnonymousSelector<T>(IEnumerable<string> propertyNames) { var propList = propertyNames.OrderBy(p => p).ToList(); // 生成缓存键,确保相同属性列表对应同一类型 string cacheKey = $"{typeof(T).FullName}_{string.Join("|", propList)}"; Type anonymousType; lock (_cacheLock) { if (!_anonymousTypeCache.TryGetValue(cacheKey, out anonymousType)) { var propertyInfos = propList.Select(name => typeof(T).GetProperty(name, BindingFlags.Public | BindingFlags.Instance)) .Where(p => p != null) .ToList(); if (propertyInfos.Count != propList.Count) { var missingProps = propList.Except(propertyInfos.Select(p => p.Name)); throw new ArgumentException($"类型 {typeof(T).Name} 中不存在以下属性:{string.Join(", ", missingProps)}"); } anonymousType = CreateAnonymousType(propertyInfos); _anonymousTypeCache.Add(cacheKey, anonymousType); } } ParameterExpression param = Expression.Parameter(typeof(T), "x"); // 构建属性访问表达式和成员绑定 MemberBinding[] bindings = propList.Select(name => { PropertyInfo sourceProp = typeof(T).GetProperty(name); PropertyInfo targetProp = anonymousType.GetProperty(name); return Expression.Bind(targetProp, Expression.Property(param, sourceProp)); }).ToArray(); // 构建匿名类型初始化表达式并转换为object NewExpression newExpr = Expression.MemberInit(Expression.New(anonymousType.GetConstructors()[0]), bindings); UnaryExpression convertExpr = Expression.Convert(newExpr, typeof(object)); return Expression.Lambda<Func<T, object>>(convertExpr, param); } private static Type CreateAnonymousType(List<PropertyInfo> properties) { // 动态生成类似匿名类型的密封类 var assemblyName = new AssemblyName($"DynamicAnon_{Guid.NewGuid()}"); var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name); // 类型命名模仿C#匿名类型规则 var typeName = $"<>f__AnonymousType{properties.Count}_{Guid.NewGuid():N}"; var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Class | TypeAttributes.Serializable); var ctorParamTypes = new List<Type>(); foreach (var prop in properties) { // 创建私有只读字段 string fieldName = $"<{prop.Name}>i__Field"; var fieldBuilder = typeBuilder.DefineField(fieldName, prop.PropertyType, FieldAttributes.Private | FieldAttributes.InitOnly); // 创建公共getter属性 var propBuilder = typeBuilder.DefineProperty(prop.Name, PropertyAttributes.None, prop.PropertyType, null); var getterBuilder = typeBuilder.DefineMethod($"get_{prop.Name}", MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, prop.PropertyType, Type.EmptyTypes); var getterIl = getterBuilder.GetILGenerator(); getterIl.Emit(OpCodes.Ldarg_0); getterIl.Emit(OpCodes.Ldfld, fieldBuilder); getterIl.Emit(OpCodes.Ret); propBuilder.SetGetMethod(getterBuilder); ctorParamTypes.Add(prop.PropertyType); } // 创建匹配的构造函数 var ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, ctorParamTypes.ToArray()); var ctorIl = ctorBuilder.GetILGenerator(); for (int i = 0; i < properties.Count; i++) { ctorIl.Emit(OpCodes.Ldarg_0); ctorIl.Emit(OpCodes.Ldarg, i + 1); var field = typeBuilder.GetField($"<{properties[i].Name}>i__Field"); ctorIl.Emit(OpCodes.Stfld, field); } ctorIl.Emit(OpCodes.Ret); return typeBuilder.CreateType(); } }
关键说明
- 类型缓存:通过属性列表和类型
T生成唯一缓存键,避免重复创建动态类型,提升性能。 - 动态类型构建:使用
TypeBuilder生成和C#匿名类型结构一致的密封类,包含私有只读字段、公共getter属性和匹配构造函数。 - 表达式构建:通过
Expression.MemberInit将属性访问结果绑定到动态类型的属性上,最后转换为object类型满足Func<T, object>的返回要求。
内容的提问来源于stack exchange,提问作者DerpDerp
相关产品推荐
相关产品推荐

