Подтвердить что ты не робот

Как я могу перегрузить конструктор как конструктор по умолчанию, так и перегрузку базового конструктора?

Возможно, вопрос, который я изложил, не является правильным вопросом, потому что я уже знаю, что короткий ответ - "вы не можете".

Ситуация

У меня есть базовый класс с перегруженным конструктором, который принимает два аргумента.

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()

Я делаю что-то неправильно?

Мне интересно, если что-то не так с моим дизайном, что заставляет меня хотеть назвать базовую (адрес, цену) и это(). Может быть, я не должен устанавливать BuildingType в конструкторе, а где-то еще? Я попытался ввести поле для этого.

    public BuildingType BuildingType = BuildingType.General;

Но тогда я не могу сделать то же самое в дочернем классе. Я бы скрывал поле BuildingType в базовом классе, поэтому мне пришлось бы использовать оператор new в дочернем классе. Я попытался сделать BuildingType в базовом классе virtual, но поле не может быть создано виртуальным.

Создание чего-то в базовом конструкторе

В этом простом примере конструкторы по умолчанию присваивают значения по умолчанию некоторым свойствам. Но конструктор Building мог бы создать фонд для здания, а конструктор по умолчанию Office мог бы создать... (не могу придумать что-то, но вы получите идею). Итак, вы все равно хотите выполнить оба конструктора по умолчанию.

Я думаю здесь не в том направлении?


Update

На основе ответа Джона Скита и комментариев, вот мой новый код. Я изменил цепочку конструкторов от наименее специфичной до самой конкретной. Я также добавил BuildingType к конструктору класса Building, сделал этот конструктор защищенным и сделал private setter.

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;
    }
}

Можете ли вы (Джон Скит или кто-то еще) прокомментировать эту пересмотренную версию кода?

Одна (второстепенная) проблема, которая не решается этим, заключается в том, что конструктор по умолчанию для класса Office по-прежнему должен предоставлять адрес по умолчанию ("Unknown Office" в приведенном выше коде). Я бы предпочел, чтобы конструктор базового класса принял решение по адресу, если он не указан. Таким образом, этот код по-прежнему не выполняет именно то, что я хочу.

Я мог бы, вероятно, решить это, не используя цепочку конструкторов в производном классе, но вместо этого каждый из его конструкторов напрямую вызовет базовый конструктор. Это означало бы, что я изменил бы конструктор по умолчанию класса Office на

public Office() : base(BuildingType.Office)

Это будет работать для этого простого примера, но если есть какой-то метод, который я хотел бы выполнить при каждом экземпляре Office, мне пришлось бы звонить во всех конструкторах. Вот почему конструкторская цепочка звучит как лучшая идея для меня.

4b9b3361

Ответ 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.)

Ответ 2

Создайте закрытый метод, который присваивает значения по умолчанию всем вашим свойствам и вызывает их от каждого конструктора отдельно.