如何编写可修改类字段的通用方法?解决重复代码问题
问题描述
存在大量重复代码场景,示例如下:
if (claimSettingHistoryDto.NewClaimTypeName == claimSettingHistoryDto.OldClaimTypeName) { claimSettingHistoryDto.NewClaimTypeName = null; claimSettingHistoryDto.OldClaimTypeName = null; } if (claimSettingHistoryDto.NewApplicantName == claimSettingHistoryDto.OldApplicantName) { claimSettingHistoryDto.NewApplicantName = null; claimSettingHistoryDto.OldApplicantName = null; } if (claimSettingHistoryDto.NewDamageSparePartsTotalCostInsertion == claimSettingHistoryDto.OldDamageSparePartsTotalCostInsertion) { claimSettingHistoryDto.NewDamageSparePartsTotalCostInsertion = null; claimSettingHistoryDto.OldDamageSparePartsTotalCostInsertion = null; }
这类“对比新旧字段值,相等则将两个字段设为null”的逻辑在不同类的不同字段中频繁出现。尝试实现的通用方法因仅修改局部变量无法生效:
private void SetNull(object newData, object oldData) { if (newData == oldData) { newData = null; oldData = null; } }
需要实现能够直接修改类字段的通用方法。
解决方案
方案一:委托传递(性能最优,直观可控)
通过委托传递字段的读取和写入逻辑,直接操作目标对象的字段,避免值传递的局限:
// 泛型版本,支持任意引用类型字段 private void SetNullIfEqual<TObj, TProp>(TObj obj, Func<TObj, TProp> getNewValue, Action<TObj, TProp> setNewValue, Func<TObj, TProp> getOldValue, Action<TObj, TProp> setOldValue) where TProp : class { var newVal = getNewValue(obj); var oldVal = getOldValue(obj); if (Equals(newVal, oldVal)) { setNewValue(obj, null); setOldValue(obj, null); } }
调用示例:
SetNullIfEqual(claimSettingHistoryDto, dto => dto.NewClaimTypeName, (dto, val) => dto.NewClaimTypeName = val, dto => dto.OldClaimTypeName, (dto, val) => dto.OldClaimTypeName = val);
此方案性能与手动编写逻辑一致,且能明确控制要处理的字段,适合零散字段的处理场景。
方案二:反射批量处理(通用性最强)
如果对象中的新旧字段遵循统一命名规则(如NewXXX和OldXXX配对),可通过反射批量遍历并处理所有符合规则的字段:
private void SetNullEqualFields<T>(T obj) { var type = typeof(T); // 获取所有公共实例属性(如果是字段则用GetFields) var newProps = type.GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.Name.StartsWith("New")) .ToList(); foreach (var newProp in newProps) { // 匹配对应的Old属性 var oldPropName = newProp.Name.Replace("New", "Old"); var oldProp = type.GetProperty(oldPropName, BindingFlags.Public | BindingFlags.Instance); if (oldProp == null) continue; var newVal = newProp.GetValue(obj); var oldVal = oldProp.GetValue(obj); if (Equals(newVal, oldVal)) { newProp.SetValue(obj, null); oldProp.SetValue(obj, null); } } }
调用示例:
SetNullEqualFields(claimSettingHistoryDto);
此方案无需逐个指定字段,但反射存在一定性能开销,适合批量处理或非高频调用场景。
方案三:表达式树(兼顾通用性与性能)
通过表达式树在运行时生成编译后的委托,既保留反射的通用性,又接近手动代码的性能:
// 编译生成处理指定字段的委托,可缓存复用 private static Action<T> CreateSetNullAction<T>(string newPropName, string oldPropName) { var objParam = Expression.Parameter(typeof(T), "obj"); var newPropExpr = Expression.Property(objParam, newPropName); var oldPropExpr = Expression.Property(objParam, oldPropName); // 构建相等判断与赋值逻辑 var equalCondition = Expression.Equal(newPropExpr, oldPropExpr); var setNewNull = Expression.Assign(newPropExpr, Expression.Constant(null, newPropExpr.Type)); var setOldNull = Expression.Assign(oldPropExpr, Expression.Constant(null, oldPropExpr.Type)); var ifBlock = Expression.IfThen(equalCondition, Expression.Block(setNewNull, setOldNull)); return Expression.Lambda<Action<T>>(ifBlock, objParam).Compile(); }
调用示例:
// 编译一次后可重复调用,建议缓存委托实例 var processClaimTypeAction = CreateSetNullAction<ClaimSettingHistoryDto>("NewClaimTypeName", "OldClaimTypeName"); processClaimTypeAction(claimSettingHistoryDto);
此方案适合需要多次处理同类型同字段的场景,编译后的委托性能接近手动代码。
内容的提问来源于stack exchange,提问作者Yuriy Skripnik
相关产品推荐
相关产品推荐

