在构造函数外部设置只读字段的可接受方法

jdp*_*nix 8 c# constructor readonly switch-statement

我有一个构造函数,在这样的开关上执行初始化:

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 
        } 
    }
}
Run Code Online (Sandbox Code Playgroud)

我仍然在实现这一点,但一旦完成它将很容易就是几百行.我不喜欢这么大的构造函数,但我不知道如何安全地绕过这个语言特性(并且完全绕过我不想做的事情.)也许应该是暗示我正在尝试做的事情存在根本性的错误,但我不确定.

基本上,我想在我自己的自定义不可变类型中执行复杂的初始化.最好的方法是什么?在这种情况下,无数行数构造函数是一件可怕的事情吗?

更新:为了澄清,我想要做的是在一个类中保持不变性,这个类将以尽可能最好的方式以复杂的方式初始化实例.我正在编写一个代表随机生成的标记的类,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". 
Run Code Online (Sandbox Code Playgroud)

最终产生这个问题的类是代表每个标记的类.初始化问题来自能够支持各种格式(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."); 
        }
    }
Run Code Online (Sandbox Code Playgroud)

readonly最初用过,因为我误解了使用它的原因.简单地删除readonly和替换自动属性({ get; private set; }即将照顾我的不变性问题.

这个问题已成为一个关于初始化任务的问题,而不是关于不变性的问题FormatToken.也许'如何执行复杂的,可能未知的初始化'现在是一个更好的问题标题.我现在完全明白,拥有一个巨大的开关是一个坏主意.工厂模式对我正在做的事情肯定很有吸引力,我想我回答了我的问题.我只想再给它几天.

非常感谢你到目前为止的想法!我将离开最初的示例代码以保持答案有意义.

小智 7

您可以在Foo类上使用静态工厂方法并结合私有构造函数.工厂方法应负责执行大型开关,找出所需的Bar和Baz值,然后简单地将计算值传递给私有构造函数.

当然,这并没有摆脱巨型开关,但它完全将其移出构造函数,在构造函数中我们通常被告知进行大型计算是不好的.

这样你就会得到像这样的东西

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);
    }
}
Run Code Online (Sandbox Code Playgroud)


Min*_*ang 5

您可以使用自动属性:

public int Bar { get; private set; }.你已经在资本化,Bar就像它是一个财产一样.其他类可以获得Bar,但只有你的类可以设置Bar由于它的private set;setter.

但是,您可以Bar为每个对象设置多次的值.

readonly如果你使用构造函数Micha的方式(/sf/answers/1350174801/),你可以在方法中设置自动属性(但不能使用).


Nah*_*hum -2

switch 语句根本上是错误的!

请参阅解释: https://softwareengineering.stackexchange.com/questions/147214/refactoring-switch-statements-and-is-there-any-real-use-for-switch-statements-at

这是一种方法:(基类) http://simpleprogrammer.com/2012/02/21/refactoring-switches-to-classes/

这是其他方法:(工厂) https://softwareengineering.stackexchange.com/questions/147214/refactoring-switch-statements-and-is-there-any-real-use-for-switch-statements-at

  • 我看不出该链接如何解释“switch”语句总是不好的。正确使用它们可以简单地条件代码,特别是当被切换的“枚举”包含按位标志时。 (5认同)