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

如何在C#中实现仿JS Object.assign的可选可重写配置类

在C#中实现类似JavaScript的动态对象覆盖默认配置功能

需求说明

在JavaScript中,可通过Object.assign传入动态对象,仅覆盖目标对象的指定成员,其余保留默认值。示例代码如下:

class Options
{
    constructor()
    {
        this.foo = 123;
        this.fee = false;
        this.nothing = 1.23;
        this.thing = new MouseEvent();
    }
}

var globalOps = new Options();

function doTest(ops = null)
{
    var currentOps = Object.assign({}, globalOps); // 克隆避免修改全局配置
    if(ops != null)
        Object.assign(currentOps, ops);

    console.log(currentOps.foo); // 输出555 - 默认值123
    console.log(currentOps.fee); // 输出true - 默认值false
    console.log(currentOps.nothing); // 输出1.23 - 保持默认
    console.log(currentOps.thing); // 输出null - 默认是MouseEvent实例
}

globalOps.fee = true;

doTest({
    foo: 555,
    thing: null
});

提问者尝试过多种方案但均不理想,希望找到C#中类型安全、有智能提示、兼容.NET Framework(或适配高版本.NET)、可读性强的实现方式。

解决方案

方案1:匿名类型+反射赋值(兼容.NET Framework)

利用匿名类型的强类型特性(自带智能提示),结合反射将匿名对象的属性值复制到默认配置的克隆实例上,完美匹配需求:

1. 定义配置类

public class Options
{
    public int Foo { get; set; } = 123;
    public bool Fee { get; set; } = false;
    public double Nothing { get; set; } = 1.23;
    public MouseEvent Thing { get; set; } = new MouseEvent();

    // 实现浅克隆,避免修改全局实例
    public Options Clone()
    {
        return (Options)MemberwiseClone();
    }
}

2. 编写反射扩展方法

public static class ObjectExtensions
{
    // 将匿名对象的属性值复制到目标对象
    public static T ApplyOverrides<T>(this T target, object overrides) where T : class
    {
        if (overrides == null) return target;

        var targetType = typeof(T);
        var overrideType = overrides.GetType();

        foreach (var overrideProp in overrideType.GetProperties())
        {
            var targetProp = targetType.GetProperty(overrideProp.Name);
            if (targetProp != null && targetProp.CanWrite)
            {
                var value = overrideProp.GetValue(overrides);
                targetProp.SetValue(target, value);
            }
        }

        return target;
    }
}

3. 使用示例

// 全局配置实例
public static Options GlobalOps = new Options();

public void DoTest(object overrides = null)
{
    // 克隆全局配置,再应用覆盖项
    var currentOps = GlobalOps.Clone().ApplyOverrides(overrides);

    Console.WriteLine(currentOps.Foo); // 输出555
    Console.WriteLine(currentOps.Fee); // 输出true(全局已修改)
    Console.WriteLine(currentOps.Nothing); // 输出1.23
    Console.WriteLine(currentOps.Thing); // 输出null
}

// 调用方式(匿名类型有智能提示)
GlobalOps.Fee = true;
DoTest(new { Foo = 555, Thing = (MouseEvent)null });

优势:兼容.NET Framework,无额外依赖,调用语法接近JavaScript,智能提示完善。


方案2:记录类型With表达式(.NET 6+)

如果使用.NET 6及以上版本,可利用记录类型的With表达式实现不可变配置的部分更新,语法简洁且类型安全:

1. 定义记录类型配置

public record Options
{
    public int Foo { get; init; } = 123;
    public bool Fee { get; init; } = false;
    public double Nothing { get; init; } = 1.23;
    public MouseEvent Thing { get; init; } = new MouseEvent();
}

2. 使用示例

// 全局配置实例
public static Options GlobalOps = new Options();

public void DoTest(Options overrides = null)
{
    // 直接基于全局配置创建新实例,仅覆盖指定成员
    var currentOps = overrides == null 
        ? GlobalOps 
        : GlobalOps with
        {
            Foo = overrides.Foo,
            Thing = overrides.Thing
        };

    Console.WriteLine(currentOps.Foo);
    Console.WriteLine(currentOps.Fee);
    Console.WriteLine(currentOps.Nothing);
    Console.WriteLine(currentOps.Thing);
}

// 调用方式
GlobalOps = GlobalOps with { Fee = true };
DoTest(new Options { Foo = 555, Thing = null });

// 更简洁的调用:直接基于全局配置修改
DoTest(GlobalOps with { Foo = 555, Thing = null });

优势:语法简洁,原生支持不可变对象更新,类型安全。


方案3:Builder模式(适合复杂配置)

若配置项较多或需要更灵活的配置逻辑,Builder模式是最优选择,链式调用体验好,可读性强:

1. 定义配置类与Builder

public class Options
{
    public int Foo { get; set; } = 123;
    public bool Fee { get; set; } = false;
    public double Nothing { get; set; } = 1.23;
    public MouseEvent Thing { get; set; } = new MouseEvent();

    // 私有构造,强制通过Builder创建实例
    private Options() { }

    // 浅克隆方法
    public Options Clone()
    {
        return (Options)MemberwiseClone();
    }

    public class Builder
    {
        private readonly Options _options;

        // 基于基础配置初始化Builder
        public Builder(Options baseOptions)
        {
            _options = baseOptions.Clone();
        }

        // 链式设置方法
        public Builder SetFoo(int value)
        {
            _options.Foo = value;
            return this;
        }

        public Builder SetThing(MouseEvent value)
        {
            _options.Thing = value;
            return this;
        }

        // 可添加更多Set方法适配其他属性

        // 构建最终配置
        public Options Build()
        {
            return _options;
        }
    }
}

2. 使用示例

// 全局配置实例
public static Options GlobalOps = new Options();

public void DoTest(Action<Options.Builder> configure = null)
{
    var builder = new Options.Builder(GlobalOps);
    configure?.Invoke(builder);
    var currentOps = builder.Build();

    Console.WriteLine(currentOps.Foo);
    Console.WriteLine(currentOps.Fee);
    Console.WriteLine(currentOps.Nothing);
    Console.WriteLine(currentOps.Thing);
}

// 调用方式(链式调用,智能提示完善)
GlobalOps.Fee = true;
DoTest(builder => builder.SetFoo(555).SetThing(null));

优势:类型安全,扩展性强,适合复杂配置场景,代码可读性极高。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 07:45:51