【发布时间】:2014-07-29 19:24:01
【问题描述】:
我有一个像这样在开关上执行初始化的构造函数:
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 的不变性的问题。也许“如何执行复杂的、可能未知的初始化”现在是一个更好的问题标题。现在对我来说很明显,拥有一个巨大的开关是一个坏主意。工厂模式对于我正在做的事情当然很有趣,我认为回答了我的问题。我只想再给它几天。
非常感谢您到目前为止的想法!我将最初的示例代码留在此处,以使答案有意义。
【问题讨论】:
-
Readonly 表示只读...您将无法设置它。但是为什么不为您的财产使用公共 getter 和私人 setter 呢?您还可以使用将 out 参数设置为您的属性的方法:Init(out this.bar)
-
严格来说这不是真的,只读值可以在构造函数中初始化。
-
我们在这里谈论什么样的初始化?只需设置默认值即可通过属性公开。
-
最简单的选择是将解析和验证移到单独的方法中,我是小型构造函数的粉丝。我仍然会担心构造函数中的逻辑过多可能会影响性能。也许您可以通过限制输入选项来最小化所需的逻辑,例如对 bar 等使用枚举。如果没有此类使用的更具体示例,就很难说。
-
@codemonkeh:我同意将事情转移到一个单独的方法中 - 但在得到证明之前我不会担心性能。如果工作需要完成,就必须完成 - 在构造函数中比在其他任何地方都没有更多的性能负担。
标签: c# constructor switch-statement readonly