0

I have a immutable class 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);
    }
    ......

}

If i need to change one field, field1 for example, i need to return new instance of class from the method. All five field pass to constructor, even if four of them haven't been changed.

It's ok, if i use 5 fields. But if i use 40 fields and i can't pass 40 parametrs to constructor. What i need to do? How can I save values of other fields and create new instance without passes parameters to constructor?

There is I really need immutable class. It's important condition of my work.

murzagurskiy
  • 1,273
  • 1
  • 20
  • 44
  • you could use reflection but honestly I would not - the question is why you ever need a class with 40 fields in the first place ;) (side-note you can have exactly that with F# if you want it's as easy as `let resetField3 = { oldF1 with field3 = 42 }` - so if you really want to work efficient with immutable data than maybe a look at F# will not hurt you at all ^^) – Random Dev Sep 27 '15 at 17:44
  • and yeah you can write you a single function with nullable arguments and the original copy - so you have to write the boilerplate only once (just set the not-null arguments and take the rest from the copy) - maybe some automapper could help you with that, but as I said reflection and stuff ... nah – Random Dev Sep 27 '15 at 17:47
  • I believe you need a deep copy. http://stackoverflow.com/questions/129389/how-do-you-do-a-deep-copy-an-object-in-net-c-specifically use Deepclone method from accepted answer and just send object itself into it. 'return DeepCopy(this);'. Or first store it in variable then change assing fields to copied object and return it – M.kazem Akhgary Sep 27 '15 at 18:05
  • When i said "40 fields" i only mean situation, when number of parameters is too big to pass it in constructor. So, constructor with nullable parameters is interesting idea. I will try. – murzagurskiy Sep 27 '15 at 18:52

2 Answers2

1

Use the Object.MemberwiseClone() method every class inherits from 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;
    }

    ...
}

You reach immutability by exposing the fields through a read-only property (a property with only a getter and no setter).

Note that MemberwiseClone only makes a shallow copy of the original object. This is ok, if this object only contains fields of value type or if all the reference type fields are immutable as well (like string, for instance). Otherwise you might have to clone these fields too.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
1

A class with 40 fields seems very complex, try to refactor it into several smaller classes.

To avoid writing too much boilerplate code, you can use optional nullable parameters (Carsten suggested sth similar in his comment)

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);
    }
}

You could also use mutable fields with private setters. This will allow you to use tools like automapper to create a copy and set the modified fields before returning the new instance. Such class however could be mutated using reflection.

Jakub Lortz
  • 14,616
  • 3
  • 25
  • 39