Non è possibile utilizzare gli inizializzatori di oggetto con oggetti immutabili. Richiedono proprietà modificabili.
Un oggetto immutabile implica "non cambia dopo la creazione". Rendere Name
un parametro del costruttore esprime perfettamente questo principio.
Se l'oggetto diventa troppo complicato per un costruttore comprensibile, è anche possibile utilizzare il modello Generatore. Generalmente, il builder avrà proprietà mutabili (che è possibile utilizzare negli inizializzatori di oggetti) e il suo metodo .Build() creerà l'istanza effettiva.
MODIFICA (OP): Aggiungo il mio esempio di generatore che ho preparato qui, quindi accetto questa risposta poiché propone una soluzione ragionevole.
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; }
}
È possibile quindi costruire un'istanza con il seguente:
MyObject test = new MyObject.Builder { Name = "foo" }.Build();
EDIT: questo è il mio introito sul modello:
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();
}
Ecco il vostro esempio come implementato con Builder<T>
. Si avvale delle regole di scoping di tipi nidificati per accedere al setter privato:
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; }
}
}
}
Ha una conversione implicita per il tipo di destinazione, che consente di fare questo:
MyObject myObject = new MyObject.Builder { Name = "Some name" };
O questo:
public void Foo(MyObject myObject)
// ...
Foo(new MyObject.Builder { Name = "Some name" });
Ho aggiornato la mia risposta per includere la mia versione del modello di builder. Spero che sia d'aiuto. –