能否创建根据输入返回大型对象指定属性的高效方法?
这个需求太常见了,完全不用写一堆重复的方法来实现!我给你分享几个实用的方案,既能满足按需填充属性的需求,又能兼顾代码的可维护性和性能:
方案一:基于反射的快速实现
这是最直接的实现方式,利用反射遍历传入的属性名,根据属性类型区分简单类型和复杂类型的填充逻辑:
public MyObject FillSelectProperties(Guid id, params string[] properties) { var myObj = new MyObject(); foreach (var propName in properties) { // 获取对应属性的元信息 var propInfo = typeof(MyObject).GetProperty(propName); if (propInfo == null) continue; // 可根据需求改为抛出异常,避免传入无效属性名 // 区分简单类型和复杂类型处理 if (IsSimpleType(propInfo.PropertyType)) { // 简单类型:从数据库查询单个字段值 var value = GetSimplePropertyValueFromDb(id, propName); propInfo.SetValue(myObj, value); } else { // 复杂类型:调用对应数据库方法获取关联对象 var complexObj = GetComplexPropertyFromDb(id, propInfo.PropertyType); propInfo.SetValue(myObj, complexObj); } } return myObj; } // 辅助方法:判断是否为简单类型(值类型、字符串、枚举) private bool IsSimpleType(Type type) { return type.IsValueType || type == typeof(string) || type.IsEnum; } // 模拟简单属性的数据库查询逻辑 private object GetSimplePropertyValueFromDb(Guid id, string propName) { using (var db = new YourDbContext()) { // 用EF Core的EF.Property实现动态查询单个字段 return db.MyObjects.Where(o => o.Id == id) .Select(o => EF.Property<object>(o, propName)) .FirstOrDefault(); } } // 模拟复杂属性的数据库查询逻辑 private object GetComplexPropertyFromDb(Guid id, Type complexType) { // 根据复杂类型的不同,调用对应的查询逻辑 if (complexType == typeof(Customer)) { using (var db = new YourDbContext()) { return db.Customers.FirstOrDefault(c => c.MyObjectId == id); } } // 其他复杂类型可在此扩展 return null; }
优点:代码简洁,无需为每个属性单独写方法,快速实现需求;
缺点:反射存在一定性能开销,如果是高频调用场景,可能需要优化。
方案二:表达式树缓存优化性能
反射的性能瓶颈在于每次都要查找属性元信息,我们可以用表达式树把属性赋值逻辑编译成委托并缓存,后续调用直接执行委托,性能接近直接调用:
// 缓存已编译的属性赋值委托,避免重复编译 private static readonly Dictionary<string, Action<MyObject, object>> _propertySetters = new Dictionary<string, Action<MyObject, object>>(); public MyObject FillSelectPropertiesWithExpression(Guid id, params string[] properties) { var myObj = new MyObject(); foreach (var propName in properties) { // 先从缓存取委托,没有则编译并缓存 if (!_propertySetters.TryGetValue(propName, out var setter)) { var propInfo = typeof(MyObject).GetProperty(propName); if (propInfo == null) continue; // 构建表达式树:将object类型的值转换为属性类型并赋值 var objParam = Expression.Parameter(typeof(MyObject), "obj"); var valueParam = Expression.Parameter(typeof(object), "value"); var convertExpr = Expression.Convert(valueParam, propInfo.PropertyType); var assignExpr = Expression.Assign(Expression.Property(objParam, propInfo), convertExpr); // 编译为委托并缓存 setter = Expression.Lambda<Action<MyObject, object>>(assignExpr, objParam, valueParam).Compile(); _propertySetters[propName] = setter; } // 获取属性值(复用方案一中的查询方法) object value; var propType = typeof(MyObject).GetProperty(propName).PropertyType; if (IsSimpleType(propType)) { value = GetSimplePropertyValueFromDb(id, propName); } else { value = GetComplexPropertyFromDb(id, propType); } // 执行委托赋值 setter(myObj, value); } return myObj; }
优点:第一次调用编译后,后续调用性能接近原生代码,适合高频场景;
缺点:比反射方案多了缓存逻辑,代码稍复杂,但整体仍很简洁。
方案三:类型安全的属性填充器模式
如果追求完全的类型安全,不想用反射,也可以提前为每个属性注册独立的填充器,这种方式扩展性和可维护性更好:
// 定义属性填充器接口,统一填充逻辑的规范 public interface IPropertyFiller { string PropertyName { get; } void Fill(MyObject obj, Guid id); } // 示例:IsActive属性的填充器 public class IsActivePropertyFiller : IPropertyFiller { public string PropertyName => nameof(MyObject.IsActive); public void Fill(MyObject obj, Guid id) { using (var db = new YourDbContext()) { obj.IsActive = db.MyObjects.Where(o => o.Id == id).Select(o => o.IsActive).FirstOrDefault(); } } } // 示例:Customer复杂属性的填充器 public class CustomerPropertyFiller : IPropertyFiller { public string PropertyName => nameof(MyObject.Customer); public void Fill(MyObject obj, Guid id) { using (var db = new YourDbContext()) { obj.Customer = db.Customers.FirstOrDefault(c => c.MyObjectId == id); } } } // 在服务初始化时注册所有填充器(比如静态构造函数或依赖注入容器) private static readonly Dictionary<string, IPropertyFiller> _fillers = new Dictionary<string, IPropertyFiller>(); static YourService() { _fillers.Add(nameof(MyObject.IsActive), new IsActivePropertyFiller()); _fillers.Add(nameof(MyObject.Customer), new CustomerPropertyFiller()); // 其他属性的填充器在此注册 } public MyObject FillSelectPropertiesWithFillers(Guid id, params string[] properties) { var myObj = new MyObject(); foreach (var propName in properties) { if (_fillers.TryGetValue(propName, out var filler)) { filler.Fill(myObj, id); } } return myObj; }
优点:完全类型安全,没有反射开销;每个属性的填充逻辑独立,便于单元测试和维护;
缺点:需要为每个属性编写填充器类,但比起写一堆重复的方法,这种方式更规范,扩展性更强。
内容的提问来源于stack exchange,提问作者merger
相关产品推荐
相关产品推荐

