如果你的意圖是在編譯期間禁止賦值,那麼你必須堅持構造函數賦值和私有setter。但是它有許多缺點 - 你是不是能夠使用新的成員初始化,也不XML deseralization等
我建議是這樣的:
public class IssuerRecord
{
public string PropA { get; set; }
public IList<IssuerRecord> Subrecords { get; set; }
}
public class ImmutableIssuerRecord
{
public ImmutableIssuerRecord(IssuerRecord record)
{
PropA = record.PropA;
Subrecords = record.Subrecords.Select(r => new ImmutableIssuerRecord(r));
}
public string PropA { get; private set; }
// lacks Count and this[int] but it's IReadOnlyList<T> is coming in 4.5.
public IEnumerable<ImmutableIssuerRecord> Subrecords { get; private set; }
// you may want to get a mutable copy again at some point.
public IssuerRecord GetMutableCopy()
{
var copy = new IssuerRecord
{
PropA = PropA,
Subrecords = new List<IssuerRecord>(Subrecords.Select(r => r.GetMutableCopy()))
};
return copy;
}
}
這裏IssuerRecord得多的描述和有用的。當你將它傳遞給其他地方時,你可以輕鬆創建不可變的版本。對不可變的代碼應該只有只讀邏輯,所以它不應該在乎它是否與IssuerRecord類型相同。我創建了每個字段的副本,而不僅僅是包裝對象,因爲它可能仍然在其他地方被更改,但是對於連續同步調用而言可能不是必需的。然而,將完整的不可變副本存儲到某些集合中「以後」更安全。它可能是一個包裝,但應用程序當你想要一些代碼禁止修改,但仍然有能力接收對象狀態的更新。
var record = new IssuerRecord { PropA = "aa" };
if(!Verify(new ImmutableIssuerRecord(record))) return false;
,如果你認爲在C++方面,你可以看到ImmutableIssuerRecords爲 「IssuerRecord常量」。你必須採取額外的措施來保護你的不變對象所擁有的物體,這就是爲什麼我建議爲所有的孩子創建一個副本(Subrecords示例)。
ImmutableIssuerRecord.Subrecors在這裏是IEnumerable並且沒有Count和this [],但是IReadOnlyList在4.5版本中出現,如果需要的話可以從文檔複製它(並且稍後可以輕鬆遷移)。
有其他的方法,以及,如可凍結:
public class IssuerRecord
{
private bool isFrozen = false;
private string propA;
public string PropA
{
get { return propA; }
set
{
if(isFrozen) throw new NotSupportedOperationException();
propA = value;
}
}
public void Freeze() { isFrozen = true; }
}
這使得代碼再次少可讀的,並且不提供編譯時保護。但您可以像平常一樣創建對象,然後在準備好後凍結它們。
構建模式也是需要考慮的事情,但是從我的角度來看它增加了太多的「服務」代碼。
在類的外部需要設置值,還是類本身可以設置值?如果類可以做到這一點,那麼你可以有一個無參數的構造函數。 –
請參閱:http://stackoverflow.com/questions/355172/how-to-design-an-immutable-object-with-complex-initialization?rq=1 – aquinas
你現在如何初始化這些屬性? –