【问题标题】:How can I have an overloaded constructor call both the default constructor as well as an overload of the base constructor?如何让重载的构造函数同时调用默认构造函数和基构造函数的重载?
【发布时间】:2011-05-23 17:01:10
【问题描述】:

也许我所说的问题不是正确的问题,因为我已经知道简短的回答是“你不能”。

情况

我有一个基类,它有一个带有两个参数的重载构造函数。

class Building
{
    public BuildingType BuildingType { get; protected set; }
    public string Address { get; set; }
    public decimal Price { get; set; }

    public Building()
    {
        BuildingType = BuildingType.General;
        Address = "Unknown";
    }

    public Building(string address, decimal price)
        : this()
    {
        Address = address;
        Price = price;
    }
}

该类正在使用枚举

enum BuildingType { None, General, Office, Apartment }

现在我想创建一个子类 Office,它也有一个重载的构造函数。这个子类添加了另一个属性(公司)。在这个类中,BuildingType 属性当然应该设置为 Office。这是代码。

class Office : Building
{
    public string Company { get; set; }

    public Office()
    {
        BuildingType = BuildingType.Office;
    }

    public Office(string address, decimal price, string company)
        : base(address, price)
    {
        Company = company;
        // BuildingType = BuildingType.Office; // Don't wanna repeat statement
    }
}

我想要什么以及为什么

我希望 Office 类的第二个构造函数同时执行 base(address, price) 构造函数以及 Office 类的默认构造函数。我想调用base(address, price) 构造函数,这样我就不必重复分配基类的所有属性。我想调用 Office 类的默认构造函数,因为它将 BuildingType 属性设置为 BuildingType.Office。

现在我知道我不能使用这样的东西。

public Office(string address, decimal price, string company)
    : base(address, price) this()

我是不是做错了什么?

我想知道我的设计是否有问题让我想同时调用 base(address, price) 和 this()。也许我不应该在构造函数中设置 BuildingType 而是在其他地方?我已经尝试为此引入一个字段。

    public BuildingType BuildingType = BuildingType.General;

但是我不能在子类中做同样的事情。我将在基类中隐藏 BuildingType 字段,因此我必须在子类中使用 new 运算符。我已经尝试在基类 virtual 中创建 BuildingType,但无法将字段设为虚拟。

在基础构造函数中创建一些东西

在这个简单的示例中,默认构造函数只为某些属性分配默认值。但是 Building 构造函数也可能正在为建筑物创建一个 Foundation,而 Office 默认构造函数可能会创建一个......(想不出什么,但你明白了)。那么你仍然想要执行这两个默认构造函数。

我是不是想错了方向?


更新

根据 Jon Skeet 的回答和 cmets,这是我的新代码。我已将构造函数链接从最不具体更改为最具体。我还将BuildingType 添加到Building 类的构造函数中,使该构造函数受到保护,并将属性设置器设为私有。

enum BuildingType { None, General, Office, Apartment }

class Building
{
    private const string DefaultAddress = "Unknown";

    public BuildingType BuildingType { get; private set; }
    public string Address { get; set; }
    public decimal Price { get; set; }

    #region Optional public constructors
    // Only needed if code other than subclass must 
    // be able to create a Building instance.
    // But in that case, the class itself can be abstract
    public Building() : this(DefaultAddress, 0m)
    {}

    public Building(string address, decimal price)
        : this(BuildingType.General, address, price) 
    {}
    #endregion

    protected Building(BuildingType buildingType)
        : this(buildingType, DefaultAddress, 0m) 
    {}

    protected Building(BuildingType buildingType,
        string address, decimal price)
    {
        BuildingType = buildingType;
        Address = address;
        Price = price;
    }
}

class Office : Building
{
    public string Company { get; set; }

    public Office() : this("Unknown Office", 0m, null) 
    {}

    public Office(string address, decimal price, string company)
        : base(BuildingType.Office, address, price)
    {
        Company = company;
    }
}

您(Jon Skeet 或其他人)能否对代码的修订版发表评论?

这里没有解决的一个(次要)问题是 Office 类的默认构造函数仍需要提供默认地址(上述代码中的"Unknown Office")。如果未指定地址,我仍然希望让基类的构造函数决定地址。所以这段代码仍然没有完全做我想要的。

我可以通过不在派生类中使用构造函数链接来解决这个问题,而是让每个构造函数直接调用基构造函数。这意味着我会将Office 类的默认构造函数更改为

public Office() : base(BuildingType.Office)

这适用于这个简单的示例,但如果我想在 Office 的每个实例化上执行某种方法,我就必须调用所有构造函数。这就是为什么构造函数链接对我来说听起来是一个更好的主意。

【问题讨论】:

标签: c# constructor


【解决方案1】:

您的方法不是传统的方法,它可以解决问题。与其让更具体的构造函数(有很多参数的构造函数)调用无参数的构造函数,不如反过来做——让无参数的构造函数调用另一个,提供默认值。这通常会导致每个类中的所有构造函数都调用一个“主要”构造函数(可能间接地,通过其他构造函数),并且“主要”构造函数调用会调用基本构造函数。

class Office : Building
{
    public string Company { get; set; }

    public Office() : this(null, 0m, null)
    {
    }

    public Office(string address, decimal price, string company)
        : base(address, price)
    {
        Company = company;
        BuildingType = BuildingType.Office; // Don't wanna repeat statement
    }
}

...和基类中的一样:

class Building
{
    public BuildingType BuildingType { get; protected set; }
    public string Address { get; set; }
    public decimal Price { get; set; }

    public Building() : this("Unknown", 0m)
    {
    }

    public Building(string address, decimal price)
    {
        BuildingType = BuildingType.General;
        Address = address;
        Price = price;
    }
}

(我会认真考虑让Building 构造函数也包含BuildingType 参数。)

【讨论】:

  • 如果我在构造函数中包含一个BuildingType 参数,那不是可以创建一个“认为”它是Office 的Building 的实例吗?另外,我想你的意思是 this(null, 0, null)?
  • 类 Office 没有采用一个参数的构造函数。你的意思可能是这个(null, 0m, null)
  • @comecme:如果你让构造函数受到保护,那么只有继承者才能使用它。
  • @comecme:您已经可以使用子类来做到这一点。一定要让这样的构造函数受到保护,这样只有一个子类才能指定建筑类型。但是您可以将其设为私有设置器,并确保您只设置一次。目前建筑物可以随意改变类型,我怀疑这不是一个好主意。
  • @Jon Skeet:您的答案的一个缺点可能是基类和子类都必须提供地址和价格的默认值。此外,如果 Building 对构造函数有更多重载,它们都必须使用相同的 "Unknown" 调用相同的构造函数。当然,我可以为此使用常量,但仍然......
【解决方案2】:

创建一个私有方法,为所有属性分配默认值,并分别从每个构造函数中调用它们。

【讨论】:

  • 当然会有所帮助。但是,对我来说,这更像是一种解决方法,而不是实际的解决方案。
  • 如果您决定将这些属性/成员变量中的一个或多个设为只读,则调用方法来分配值而不是链接构造函数将导致问题。它们只能在构造函数或声明期间分配。在这种情况下不是问题,但如果您决定在未来进行此类更改,它确实会强制您更改类的公共接口。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-05
  • 1970-01-01
相关资源
最近更新 更多