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

如何通过指定路径修改对象字段?能否预定义路径批量赋值?

优雅批量修改C#对象字段的实现方案

问题根源

你尝试用Func<Item, int>集合批量处理属性,但Func只能读取属性值,无法作为赋值语句的左值,因此触发CS0131错误。要实现批量修改,需要同时封装属性读取和属性赋值的操作。

解决方案

方案1:用ValueTuple封装读写操作(最直接)

直接定义包含读取器(Getter)和写入器(Setter)的元组集合,循环中完成计算与赋值:

class Item
{
    public int Value1 { get; set; }
    public int Value2 { get; set; }
    public int Value3 { get; set; }
    public int Value4 { get; set; }
}

var targetItem = new Item();
var common1 = new Item();
var common2 = new Item();

// 定义每个属性的读写操作
var propertyOps = new List<(Func<Item, int> Getter, Action<Item, int> Setter)>
{
    (i => i.Value1, (i, val) => i.Value1 = val),
    (i => i.Value2, (i, val) => i.Value2 = val),
    (i => i.Value3, (i, val) => i.Value3 = val),
    (i => i.Value4, (i, val) => i.Value4 = val)
};

foreach (var op in propertyOps)
{
    var newValue = Do(op.Getter);
    op.Setter(targetItem, newValue);
}

int Do(Func<Item, int> func)
{
    // 复杂业务逻辑
    return func(common1) + func(common2);
}

方案2:自定义Accessor类(更清晰)

封装读写逻辑为独立类,提升代码可读性和复用性:

public class PropertyAccessor<TObj, TProp>
{
    public Func<TObj, TProp> Get { get; }
    public Action<TObj, TProp> Set { get; }

    public PropertyAccessor(Func<TObj, TProp> get, Action<TObj, TProp> set)
    {
        Get = get;
        Set = set;
    }
}

// 使用示例
var accessors = new List<PropertyAccessor<Item, int>>
{
    new PropertyAccessor<Item, int>(i => i.Value1, (i, val) => i.Value1 = val),
    new PropertyAccessor<Item, int>(i => i.Value2, (i, val) => i.Value2 = val),
    new PropertyAccessor<Item, int>(i => i.Value3, (i, val) => i.Value3 = val),
    new PropertyAccessor<Item, int>(i => i.Value4, (i, val) => i.Value4 = val)
};

foreach (var accessor in accessors)
{
    var newValue = Do(accessor.Get);
    accessor.Set(targetItem, newValue);
}

方案3:表达式树自动生成Setter(最简洁)

利用表达式树自动生成Setter,避免手动编写赋值逻辑,适合大量属性的场景:

using System.Linq.Expressions;

public static class PropertyAccessorHelper
{
    public static PropertyAccessor<TObj, TProp> Create<TObj, TProp>(Expression<Func<TObj, TProp>> propertyExpr)
    {
        var getFunc = propertyExpr.Compile();
        
        // 动态生成赋值表达式
        var objParam = Expression.Parameter(typeof(TObj));
        var valParam = Expression.Parameter(typeof(TProp));
        var assignExpr = Expression.Assign(
            Expression.Property(objParam, ((MemberExpression)propertyExpr.Body).Member),
            valParam
        );
        var setAction = Expression.Lambda<Action<TObj, TProp>>(assignExpr, objParam, valParam).Compile();
        
        return new PropertyAccessor<TObj, TProp>(getFunc, setAction);
    }
}

// 使用示例
var accessors = new List<PropertyAccessor<Item, int>>
{
    PropertyAccessorHelper.Create<Item, int>(i => i.Value1),
    PropertyAccessorHelper.Create<Item, int>(i => i.Value2),
    PropertyAccessorHelper.Create<Item, int>(i => i.Value3),
    PropertyAccessorHelper.Create<Item, int>(i => i.Value4)
};

foreach (var accessor in accessors)
{
    var newValue = Do(accessor.Get);
    accessor.Set(targetItem, newValue);
}

方案4:反射(动态场景)

如果需要动态指定属性名,可使用反射实现,但性能略低于表达式树:

var propertyNames = new List<string> { "Value1", "Value2", "Value3", "Value4" };
var itemType = typeof(Item);

foreach (var propName in propertyNames)
{
    var property = itemType.GetProperty(propName);
    if (property == null || property.PropertyType != typeof(int))
        continue;
    
    var val1 = (int)property.GetValue(common1);
    var val2 = (int)property.GetValue(common2);
    var newValue = val1 + val2;
    
    property.SetValue(targetItem, newValue);
}

选择建议

  • 属性数量少且固定:用ValueTuple方案,代码最简洁直接。
  • 需要复用读写逻辑:用自定义Accessor类,结构更清晰。
  • 属性数量多或想减少重复代码:用表达式树方案,自动生成Setter。
  • 需要动态处理属性:用反射方案,支持运行时指定属性名。

内容的提问来源于stack exchange,提问作者ilyaVlad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 09:30:36