Возможно, вопрос, который я изложил, не является правильным вопросом, потому что я уже знаю, что короткий ответ - "вы не можете".
Ситуация
У меня есть базовый класс с перегруженным конструктором, который принимает два аргумента.
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, мне пришлось бы звонить во всех конструкторах. Вот почему конструкторская цепочка звучит как лучшая идея для меня.