【问题标题】:How to avoid duplication between constructors when they call different base constructors调用不同的基本构造函数时如何避免构造函数之间的重复
【发布时间】:2020-10-22 12:58:49
【问题描述】:

我在 SO 上看到了这个问题的许多变体,但解决方案不适用于这种情况。

我有与此类似的代码,但在 Derived 中有冗长的构造函数,我想避免重复。

abstract class Base
{
    private int ID;

    protected Base( int id )
    {
        ID = id;
    }

    protected Base()
    {
        ID = GenerateID();
    }

    private int GenerateID()
    {
        return 42;
    }
}

class Derived : Base
{
    public readonly int SomeField;
    
    public Derived( int SomeFieldInitialValue ) : base()
    {
        SomeField = SomeFieldInitialValue;
    }

    public Derived( int SomeFieldInitialValue, int id ) : base( id )
    {
        // Want to remove this duplication - could be a long constructor.
        // Can't put it in a method because it does things that only constructors can do.
        SomeField = SomeFieldInitialValue;
    }
}

我无法在 Derived 中创建一个辅助方法来包含所有构造函数共有的代码,因为其中一些代码只能在构造函数中完成(例如,设置只读成员)。

我不能只使用一个基本构造函数并传递默认值,因为用于Base.ID 的值只有Base 知道(在私有GenerateID 方法中计算)。

我不能使用字段初始化器,因为字段在每个构造函数中都没有被初始化为相同的东西,并且它们的值取决于构造函数参数。

我宁愿不将 Base.GenerateID 暴露给派生类以在没有 id 传递的情况下传递,因为没有其他理由公开它,而且将基本功能推入派生类似乎很麻烦。此外,除了一个 int 之外,未来可能还有其他细节 - 根据调用 Base 构造函数的不同,可能需要或不需要运行各种其他代码 - 让 Derived 知道这似乎很糟糕。

我宁愿避免从Derived 的成员中删除readonly,因为它们是readonly 故意的。

我感觉答案将是“抱歉,C# 不允许这样做”,我将不得不在我不想选择的选项之一之间进行选择 :) 我认为该选项是最糟糕的是删除 readonly 或者将这些字段替换为 private set 属性。

【问题讨论】:

  • 添加一个选项:将基本c'tor定义为Base(int? id),如果参数为null,则调用GenerateId。此外,定义 private Derived(int SomeFieldInitialValue, int? id) 完成所有工作并从公共 c'tors 重定向到这个新的。
  • 这个问题有点太抽象了。您可以将基类重构为只有一个构造函数吗?例如。使用工厂方法创建具有生成 id 的实例。然后您可以通过调用this(...) 而不是base(...) 来链接派生类构造函数,这会将所有初始化保持在一个地方。
  • 同意 Klaus - 因为 Base 构造函数都是受保护的并且类是抽象的 - 将它们组合成一个(接受 int?)不会损害“公共”使用(不会使其不那么方便),同时解决继承类的问题。

标签: c# inheritance constructor


【解决方案1】:

如果你可以改变基类,最好只有一个构造函数。这可以提供两个选项(固定 ID 或新生成的 ID),如下所示:

abstract class Base
{
    private int ID;

    protected Base( int? id )
    {
        ID = id ?? GenerateID();
    }

    private int GenerateID()
    {
        return 42;
    }
}

同样在派生类中,定义一个(私有或受保护的)构造函数来完成所有繁重的工作,然后将公共构造函数重定向到此。

class Derived : Base
{
    public readonly int SomeField;
    
    protected Derived( int SomeFieldInitialValue, int? id ) : base(id)
    {
        SomeField = SomeFieldInitialValue;
    }
    
    public Derived( int SomeFieldInitialValue ) : this( SomeFieldInitialValue, null)
    {
    }

    public Derived( int SomeFieldInitialValue, int id ) : this( SomeFieldInitialValue, id )
    {
    }
}

【讨论】:

  • 哦。我真的很喜欢这个。谢谢!我最轻微担心它比直接调用不同的构造函数效率低(我在游戏开发中工作,所以效率是一个巨大的优先事项)但是创建对象(和调用构造函数)已经被承认很慢,所以让构造函数变慢并不重要:)
猜你喜欢
  • 1970-01-01
  • 2017-03-17
  • 2011-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多