I have the following attempt at an immutable object:
class MyObject
{
private static int nextId;
public MyObject()
{
_id = ++nextId;
}
private int _id;
public int Id { get { return _id; } }
public string Name { get; private set; }
}
Then, I try to use it like so:
MyObject o1 = new MyObject { Name = "foo" };
But the object initialiser fails because Name
's setter is private. Is there a way around this, or do I have to choose between one or the other?
Immutable Objects are those objects whose state can not be changed once they are created, for example the String class is an immutable class. Immutable objects can not be modified so they are also thread safe in concurrent execution.
The builder pattern hides the complexities of creating a class. In the case of an immutable class, it can be used to avoid constructors with too many parameters. Since the builder is not immutable, the values can be set through multiple calls.
An immutable object cannot be modified after it was created.
Now, we have an immutable class, but, it does contain a setter. This setter actively changes a member of the (final) field p.
You can't use object initializers with immutable objects. They require settable properties.
An immutable object implies "does not change after creation". Making Name
a constructor parameter neatly expresses that principle.
If the object gets too complicated for a comprehensible constructor, you can also use the Builder pattern. Generally, the builder itself will have mutable properties (that you can use in object initializers), and its .Build() method will create the actual instance.
EDIT (OP): I'm going to add my own example of a builder that I cooked up here, then accept this answer since it proposes a reasonable solution.
class MyObject
{
public class Builder
{
public Builder()
{
// set default values
Name = String.Empty;
}
public MyObject Build()
{
return new MyObject(Name);
}
public string Name { get; set; }
}
private static int nextId;
protected MyObject(string name)
{
Id = ++nextId;
Name = name;
}
public int Id { get; private set; }
public string Name { get; private set; }
}
You can then construct an instance of it with the following:
MyObject test = new MyObject.Builder { Name = "foo" }.Build();
EDIT: This is my take on the pattern:
public abstract class Builder<T>
{
public static implicit operator T(Builder<T> builder)
{
return builder.Build();
}
private bool _built;
public T Build()
{
if(_built)
{
throw new InvalidOperationException("Instance already built");
}
_built = true;
return GetInstance();
}
protected abstract T GetInstance();
}
Here is your example as implemented with Builder<T>
. It takes advantage of the scoping rules of nested types to access the private setter:
public class MyObject
{
private static int nextId;
protected MyObject()
{
Id = ++nextId;
}
public int Id { get; private set; }
public string Name { get; private set; }
public sealed class Builder : Builder<MyObject>
{
private MyObject _instance = new MyObject();
protected override MyObject GetInstance()
{
// Validate properties here
return _instance;
}
public string Name
{
get { return _instance.Name; }
set { _instance.Name = value; }
}
}
}
It has an implicit conversion to the target type, allowing you to do this:
MyObject myObject = new MyObject.Builder { Name = "Some name" };
Or this:
public void Foo(MyObject myObject)
// ...
Foo(new MyObject.Builder { Name = "Some name" });
You need to set the property in the constructor, and you don't need a separate local variable for the id:
class MyObject {
private static int nextId = 0;
public MyObject(string name) {
Id = ++nextId;
Name = name;
}
public int Id { get; private set; }
public string Name { get; private set; }
}
Creation:
MyObject o1 = new MyObject("foo");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With