【问题标题】:How can I share constructor code with readonly class members?如何与只读类成员共享构造函数代码?
【发布时间】:2015-07-21 21:50:37
【问题描述】:

我有一个类,FooBarSet,它有一个“核心”初始化逻辑块。

FooBarFooBar 组成。类FooBarSet 使用FooBar 列表初始化。 FooBarSet 可以通过单独的并行 FooBar 列表进行初始化。

理想情况下,我可以这样运行它:

public class FooBarSet
{
    private readonly List<FooBar> _list;

    // Primary constructor.
    public FooBarSet(List<FooBar> foobarList)
    {
        // Contracts and initialization...
        _list = foobarList;
    }

    // Secondary constructor.
    public FooBarSet(List<Foo> fooList, List<Bar> barList) 
    {
        // Zip a new list of new FooBars
        var zipped = fooList.Zip(barList,
                                 (foo, bar) => new FooBar(foo, bar));

        // Call primary constructor with zipped list.
        this(zipped);
    }
}

这是 C#,不是 Java,所以this(zipped) 是非法的。常见的解决方案as in this answer,就是把核心初始化拉到一个常用的私有方法中:

public class FooBarSet
{
    private readonly List<FooBar> _list;

    // Common "constructor" called from actual constructors.
    private Init(List<FooBar> foobarList)
    {
        // Contracts and initialization...
        _list = foobarList;
    }

    public FooBarSet(List<FooBar> foobarList)
    {
        Init(foobarList);
    }

    public FooBarSet(List<Foo> fooList, List<Bar> barList) 
    {
        var zipped = fooList.Zip(barList,
                                 (foo, bar) => new FooBar(foo, bar));

        Init(zipped);
    }

}

但是,这也不起作用,因为 readonly _list 字段。

假设_list必须是readonly,我怎样才能让这些构造函数共享初始化代码?

【问题讨论】:

  • _list = Init(foobarList) 在构造函数中?让Init 改为返回List&lt;FooBar&gt;
  • @RonBeyer,我非常喜欢。它适用于我的实际实现,而不仅仅是我使用的MWE。把它作为答案扔掉,只要不出现疯狂美丽的东西,它就是你的。
  • 附注:只有列表的reference是只读的,您可以随时更改contents。那么显而易见的解决方案......
  • @Clockwork-Muse,我假设您暗示删除 readonly 修饰符。在这个MWE 中,这当然是这样做的方法:-) 但在我的实际用例中,该引用是对不可变引用类型的。
  • 不,这个想法是您可以只更改内容,但仍保留引用 readonly。显然,如果实际类型是不可变的(这是不可能的),这将不起作用。

标签: c# constructor dry


【解决方案1】:

简单的答案是让Init方法返回要在构造函数中设置的值:

public class FooBarSet
{
    private readonly List<FooBar> _list;

    // Common "constructor" called from actual constructors.
    private List<FooBar> Init(List<FooBar> foobarList)
    {
        // Contracts and initialization...
        return whateverList;
    }

    public FooBarSet(List<FooBar> foobarList)
    {
        _list = Init(foobarList);
    }

    public FooBarSet(List<Foo> fooList, List<Bar> barList) 
    {
        var zipped = fooList.Zip(barList,
                                 (foo, bar) => new FooBar(foo, bar));

        _list = Init(zipped);
    }

}

【讨论】:

  • 如果您碰巧有多个readonly 成员,则扩展为每个成员都有一个Init() 方法。 (希望readonly 成员的数量不会很大或增长。)
【解决方案2】:

您可以将“拉链”代码移动到静态函数并使用它:

public class FooBarSet
{
    private readonly List<FooBar> _list;

    private static List<FooBar> Prepare( List<Foo> fooList, List<Bar> barList )
    {
        return fooList.Zip(barList, (foo, bar) => new FooBar(foo, bar));
    }

    public FooBarSet(List<Foo> fooList, List<Bar> barList) :
        this( Prepare( fooList, barList ) )
    {
    }

    public FooBarSet(List<FooBar> zippedList)
    {
        _list = zippedList;
    }
}

【讨论】:

  • 我想每次我使用“基本”构造函数语法: this( StaticMethod( bla, bloo): base( OtherStatic( bloo, blee) 时,使用它的构造函数都是空的。总是这样吗?
  • @kdbanman 不,您可以通过使用默认值调用其他构造函数并将初始化保存在一个构造函数中来将构造函数链接得非常深。
  • @RonBeyer,对不起,我不认为这就是我的意思。注意两参数构造函数的主体是空的吗?每当我设置这样的替代构造函数时(使用: this() 语法),它的主体总是空的。因此,对于这些空构造函数中的每一个,我最终都会得到一个静态方法,其中包含我放入 Java 风格的替代构造函数中的代码。
猜你喜欢
  • 2012-04-12
  • 1970-01-01
  • 2020-03-14
  • 2016-07-31
  • 2013-05-03
  • 1970-01-01
  • 1970-01-01
  • 2016-11-23
  • 2011-12-07
相关资源
最近更新 更多