因此,我们都认识到不可变类型的好处,尤其是在多线程方案中。 (或者至少我们应该都意识到这一点;请参见例如System.String。)
但是,我还没有看到太多有关创建不可变实例的讨论,特别是设计指南。
例如,假设我们要具有以下不可变的类:
class ParagraphStyle {
public TextAlignment Alignment {get;}
public float FirstLineHeadIndent {get;}
// ...
}
我见过的最常见的方法是具有可变的/不可变的“对”类型,例如可变List<T>和不可变ReadOnlyCollection<T>类型或可变StringBuilder和不可变String类型。
为了模仿这种现有模式,将需要引入某种“可变”
ParagraphStyle
类型,以“复制”成员(以提供 setter ),然后提供一个ParagraphStyle
构造函数,该结构接受可变类型作为参数。// Option 1:
class ParagraphStyleCreator {
public TextAlignment {get; set;}
public float FirstLineIndent {get; set;}
// ...
}
class ParagraphStyle {
// ... as before...
public ParagraphStyle (ParagraphStyleCreator value) {...}
}
// Usage:
var paragraphStyle = new ParagraphStyle (new ParagraphStyleCreator {
TextAlignment = ...,
FirstLineIndent = ...,
});
因此,这行得通,支持IDE内的代码完成,并使事情在构造方面相当明显……但是看起来确实是重复的。
有没有更好的办法?
例如,C#匿名类型是不可变的,并且允许使用“常规”属性 setter 进行初始化:
var anonymousTypeInstance = new {
Foo = "string",
Bar = 42;
};
anonymousTypeInstance.Foo = "another-value"; // compiler error
不幸的是,在C#中复制这些语义的最直接方法是使用构造函数参数:
// Option 2:
class ParagraphStyle {
public ParagraphStyle (TextAlignment alignment, float firstLineHeadIndent,
/* ... */ ) {...}
}
但这并不能很好地“扩展”。如果您的类型有15个属性,具有15个参数的构造函数绝非友好,为所有15个属性提供“有用的”重载是一场噩梦。我完全拒绝这个。
如果我们尝试模仿匿名类型,则似乎可以在“不可变”类型中使用“一次设置”属性,从而删除“可变”变量:
// Option 3:
class ParagraphStyle {
bool alignmentSet;
TextAlignment alignment;
public TextAlignment Alignment {
get {return alignment;}
set {
if (alignmentSet) throw new InvalidOperationException ();
alignment = value;
alignmentSet = true;
}
}
// ...
}
问题在于,属性只能设置一次(编译器当然不会提示)并且初始化不是线程安全的,这并不明显。因此,很容易添加
Commit()
方法,以便对象可以知道开发人员已完成设置属性的操作(因此,如果调用了它们的setter,则导致先前未设置的所有属性都将引发),但这似乎是一个错误。使事情变得更糟而不是更好的方法。有没有比可变/不可变类拆分更好的设计?还是我注定要处理成员重复问题?
最佳答案
在几个项目中,我使用的是流利的方法。 IE。大多数通用属性(例如名称,位置,标题)是通过ctor定义的,而其他属性则使用Set方法更新,并返回了新的不可变实例。
class ParagraphStyle {
public TextAlignment Alignment {get; private set;}
public float FirstLineHeadIndent {get; private set;}
// ...
public ParagraphStyle WithAlignment(TextAlignment ta) {
var newStyle = (ParagraphStyle)MemberwiseClone();
newStyle.TextAlignment = ta;
}
// ...
}
只要我们的类(class)真正变得一成不变,MemberwiseClone就可以了。
关于.net - 不变的类(class) build 设计,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/2196913/