在创建不可变类的新实例时保存值

本文关键字:实例 保存 不可变 创建 新实例 | 更新日期: 2023-09-27 18:14:39

我有一个不可变类F1:

public class F1
{
    public readonly int field1;
    public readonly int field2;
    public readonly int field3;
    public readonly int field4;
    public readonly int field5;
    ......
    public F1 SetField1(int f)
    {
        return new F1(f, field2, field3, field4, field5);
    }
    ......
}

如果我需要改变一个字段,例如field1,我需要从方法中返回一个新的类实例。所有五个字段都传递给构造函数,即使其中四个没有更改。

没关系,如果我使用5个字段。但是如果我使用40个字段,我不能传递40个参数给构造函数。我需要做什么?我如何保存其他字段的值,并创建新的实例没有传递参数构造函数?

有一个我确实需要的不可变类。这是我工作的重要条件。

在创建不可变类的新实例时保存值

使用Object.MemberwiseClone()方法,每个类都继承Object

public class F1
{
    private int _field1;
    public int Field1 { get return _field1; }
    private int _field2;
    public int Field2 { get return _field2; }
    private int _field3;
    public int Field3 { get return _field3; }
    ...
    public F1 SetField1(int f)
    {
        var f1 = (F1)MemberwiseClone();
        f1._field1 = f;
        return f1;
    }
    ...
}

通过只读属性(只有getter而没有setter的属性)公开字段来达到不可变性。

注意MemberwiseClone只对原始对象做一个浅拷贝。如果这个对象只包含值类型的字段,或者所有引用类型字段都是不可变的(例如string),这是可以的。否则,您可能也必须克隆这些字段。

一个有40个字段的类看起来很复杂,试着把它重构成几个更小的类。

为了避免编写太多的样板代码,您可以使用可选的可空参数(Carsten在他的评论中建议使用类似的方法)

class ImmutableClass
{
    private readonly int _field1;
    private readonly int _field2;
    public ImmutableClass(int field1, int field2)
    {
        _field1 = field1;
        _field2 = field2;
    }
    private ImmutableClass(ImmutableClass src, int? field1 = null, int? field2 = null)
    {
        _field1 = field1 ?? src._field1;
        _field2 = field2 ?? src._field2;
    }
    //choose a more domain-related name
    public ImmutableClass ModifyField1(int value)
    {
        return new ImmutableClass(this, field1: value);
    }
    //choose a more domain-related name
    public ImmutableClass ModifyField2(int value)
    {
        return new ImmutableClass(this, field2: value);
    }
    //just an example - I wouldn't do it in my code
    public ImmutableClass ModifyFields(int? field1 = null, int? field2 = null)
    {
        return new ImmutableClass(this, field1, field2);
    }
}

您还可以使用带有私有setter的可变字段。这将允许您在返回新实例之前使用automapper等工具创建副本并设置修改后的字段。但是,这样的类可以使用反射来改变。