C#中基于Expression<Func<ExpandoObject, object>>的属性筛选实现求助
解决方案
要实现从ExpandoObject中筛选指定属性并返回新的ExpandoObject,可以通过动态构建表达式树创建高效的投影委托,以下是具体实现:
核心实现代码
using System; using System.Collections.Generic; using System.Dynamic; using System.Linq.Expressions; public static class ExpandoObjectFilter { public static Expression<Func<ExpandoObject, object>> CreateProjectionExpression(List<ListSearchImportFieldModel> fields) { // 定义输入参数:源ExpandoObject var sourceParam = Expression.Parameter(typeof(ExpandoObject), "source"); // 将ExpandoObject转换为IDictionary<string, object>以访问属性 var sourceAsDict = Expression.Convert(sourceParam, typeof(IDictionary<string, object>)); // 初始化新的ExpandoObject并转为字典类型 var newExpando = Expression.New(typeof(ExpandoObject)); var newExpandoAsDict = Expression.Convert(newExpando, typeof(IDictionary<string, object>)); // 声明变量存储新的字典实例 var newDictVar = Expression.Variable(typeof(IDictionary<string, object>), "newDict"); var assignNewDict = Expression.Assign(newDictVar, newExpandoAsDict); // 构建所有属性添加逻辑 var addPropertyExpressions = new List<Expression>(); foreach (var field in fields) { if (string.IsNullOrWhiteSpace(field.Field_Name)) continue; var fieldName = Expression.Constant(field.Field_Name); // 检查源字典是否包含当前字段,避免KeyNotFoundException var containsKey = Expression.Call( sourceAsDict, typeof(IDictionary<string, object>).GetMethod("ContainsKey"), fieldName); // 从源字典取值并添加到新字典 var getValue = Expression.Index(sourceAsDict, fieldName); var addProperty = Expression.Call( newDictVar, typeof(IDictionary<string, object>).GetMethod("Add"), fieldName, getValue); // 仅当字段存在时执行添加操作 addPropertyExpressions.Add(Expression.IfThen(containsKey, addProperty)); } // 组合完整的执行块 var block = Expression.Block( new[] { newDictVar }, assignNewDict, Expression.Block(addPropertyExpressions), Expression.Convert(newDictVar, typeof(object))); return Expression.Lambda<Func<ExpandoObject, object>>(block, sourceParam); } }
使用示例
// 构造源ExpandoObject var source = new ExpandoObject() as IDictionary<string, object>; source["Name"] = "Alice"; source["Age"] = 30; source["Email"] = "alice@example.com"; source["Address"] = "123 Main St"; // 指定需要筛选的字段列表 var targetFields = new List<ListSearchImportFieldModel> { new ListSearchImportFieldModel { Field_Name = "Name" }, new ListSearchImportFieldModel { Field_Name = "Age" }, new ListSearchImportFieldModel { Field_Name = "NonExistentField" } // 不存在的字段会被自动忽略 }; // 创建投影表达式并编译为可执行委托 var projection = ExpandoObjectFilter.CreateProjectionExpression(targetFields).Compile(); // 执行筛选 var filteredResult = projection(source as ExpandoObject) as ExpandoObject; // 输出结果 var resultDict = filteredResult as IDictionary<string, object>; foreach (var kvp in resultDict) { Console.WriteLine($"{kvp.Key}: {kvp.Value}"); } // 输出: // Name: Alice // Age: 30
关键说明
- 类型转换:利用
ExpandoObject的底层实现特性,将其转为IDictionary<string, object>,通过字典的索引器和方法操作属性。 - 异常规避:通过
ContainsKey检查字段是否存在,避免处理不存在的属性时抛出KeyNotFoundException。 - 性能优化:表达式树编译后生成的委托性能接近手写代码,适合批量处理大量
ExpandoObject实例。
内容的提问来源于stack exchange,提问作者Jin Lyu
相关产品推荐
相关产品推荐

