gpt4 book ai didi

.net - 不可变类构造设计

转载 作者:行者123 更新时间:2023-12-04 21:26:43 25 4
gpt4 key购买 nike

所以我们都意识到不可变类型的好处,特别是在多线程场景中。 (或者至少我们都应该意识到这一点;参见例如 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,则会导致之前未设置的所有属性抛出),但这似乎是一种使事情变得更糟而不是更好的方法.

有没有比可变/不可变类拆分更好的设计?还是我注定要处理成员(member)重复?

最佳答案

在几个项目中,我使用了流畅的方法。 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 - 不可变类构造设计,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2196913/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com