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

时间:2015-09-27 17:40:23

标签: c# parameters constructor immutability

我有一个不可变的类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个参数传递给构造函数。我需要做什么?如何保存其他字段的值并在没有传递参数的情况下创建新实例到构造函数?

  

我真的需要不可变的课程。这是我工作的重要条件。

2 个答案:

答案 0 :(得分:1)

使用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),则可以。否则,您可能还必须克隆这些字段。

答案 1 :(得分:1)

一个包含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);
    }
}

您还可以将可变字段与私有设置器一起使用。这将允许您使用像automapper这样的工具来创建副本并在返回新实例之前设置已修改的字段。然而,这样的类可以使用反射进行变异。