我有一个构造函数可以像这样在开关上执行初始化:
class Foo {
public readonly int Bar;
public readonly object Baz;
public Foo(int bar, string baz) {
this.Bar = bar;
switch (bar) {
case 1:
// Boatload of initialization code
this.Bar = /* value based upon initialization code */
this.Baz = /* different value based upon initialization code */
case 2:
// Different boatload of initialization code
this.Bar = /* value based upon initialization code */
this.Baz = /* different value based upon initialization code */
case 3:
// Yet another...
this.Bar = /* value based upon initialization code */
this.Baz = /* different value based upon initialization code */
default:
// handle unexpected value
}
}
}
我仍在实现这个,但一旦完成,它很容易就会有几百行。我不喜欢这么大的构造函数,但我不知道如何安全地绕过这个语言特性(我根本不想绕过)。也许应该是暗示我正在尝试做的事情存在根本性错误,但我不确定。
基本上,我想在我自己的自定义不可变类型中执行复杂的初始化。最好的方法是什么?在这种情况下,大量行数构造函数是一件可怕的事情吗?
更新:
只是为了澄清,我想要做的是在一个类中保持不变性,该类将以尽可能最好的方式以复杂的方式初始化实例。我正在编写一个代表随机生成的标记 FormatToken
的类,它通常是一个字符。
复杂的初始化是解析一个格式字符串(注意,我不是试图解析一个正则表达式来生成一个随机字符串,我不想花我接下来的 20 辈子来做这个:) ).我最初写了一些可以通过构造函数参数接受输入的东西,例如
+ /// Format tokens
+ /// c{l} Lowercase Roman character in the ASCII range.
+ /// v{L} Uppercase Roman character in the ASCII range.
+ /// c Roman character in the ASCII range.
+ /// d Decimal.
+ /// d{0-9} Decimal with optional range, both minimum and maximum inclusive.
var rand = new RandomString("c{l}C{L}ddd{0-4}d{5-9}");
rand.Value == /* could equal "fz8318" or "dP8945", but not "f92781".
最终产生这个问题的类是代表每个标记的类。初始化问题来自能否支持各种格式(ASCII字符、罗马字母、小数、符号等)
这是有问题的实际代码:
internal class FormatToken {
public TokenType Specifier { get; private set; }
public object Parameter { get; private set; }
public FormatToken(TokenType _specifier, string _parameter) {
// discussion of this constructor at
// http://stackoverflow.com/questions/19288131/acceptable-way-to-set-readonly-field-outside-of-a-constructor/
Specifier = _specifier;
_init(_specifier, _parameter);
}
private void _init(TokenType _specifier, string _parameter) {
switch (_specifier) {
case TokenType.Decimal:
_initDecimalToken(_parameter);
break;
case TokenType.Literal:
Parameter = _parameter;
break;
case TokenType.Roman:
case TokenType.LowerRoman:
case TokenType.UpperRoman:
_initRomanToken(_specifier, _parameter);
break;
default:
throw new ArgumentOutOfRangeException("Unexpected value of TokenType.");
}
}
我最初使用readonly
是因为我误解了使用它的原因。只需删除 readonly
并替换为自动属性(即 { get; private set; }
即可解决我的不变性问题。
这个问题更多的是关于初始化任务的问题,而不是关于 FormatToken
的不变性的问题。也许“如何执行复杂的、可能未知的初始化”现在是一个更好的问题标题。现在对我来说很明显,拥有一个巨大的开关是一个坏主意。工厂模式对于我正在做的事情来说肯定很有趣,我认为它回答了我的问题。我只想再给它几天时间。
非常感谢您到目前为止的想法!我将初始示例代码留在此处以使答案有意义。
最佳答案
您可以将 Foo 类的静态工厂方法与私有(private)构造函数结合使用。工厂方法应该负责进行大型切换,找出 Bar 和 Baz 的所需值,然后简单地将计算值传递给私有(private)构造函数。
当然,这并没有摆脱巨大的开关,但它把它完全移出构造函数,我们通常被告知在构造函数中进行大量计算是不好的。
这样你最终会得到类似的东西
class Foo {
public readonly int Bar;
public readonly object Baz;
private Foo(int bar, string baz) {
this.Bar = bar;
this.Bas = baz;
}
public static Foo CreateFoo(int bar, string baz)
{
int tbar;
string tbaz;
switch (bar) {
case 1:
// Boatload of initialization code
tbar = /* value based upon initialization code */
tbaz = /* different value based upon initialization code */
case 2:
// Different boatload of initialization code
tbar = /* value based upon initialization code */
tbaz = /* different value based upon initialization code */
//...
default:
// handle unexpected value
}
return new Foo(tbar, tbaz);
}
}
关于c# - 在构造函数之外设置只读字段的可接受方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19288131/