You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写可修改类字段的通用方法?解决重复代码问题

问题描述

存在大量重复代码场景,示例如下:

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 16:25:25