#c# #inheritance #constructor
#c# #наследование #конструктор
Вопрос:
Я видел много вариантов этого вопроса, заданного на 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
производным классам возможность передачи в отсутствие идентификатора для передачи, поскольку нет другой причины для его раскрытия, и кажется грязным перенос базовой функциональности в производные классы. Также в будущем, вероятно, появятся другие детали, помимо простого int — есть всевозможный другой код, который может потребоваться или не потребоваться для запуска в зависимости от того, какой Base
конструктор вызывается — предоставление Derived
информации об этом кажется плохим.
Я бы предпочел не удалять члены readonly
from Derived
, поскольку они readonly
предназначены специально.
У меня такое чувство, что ответ будет «извините, C # этого не позволяет», и мне придется выбирать между одним из вариантов, которые я не хочу принимать 🙂 Я думаю, что наименее плохой вариант — удалить readonly
или, возможно, заменить эти поля private set
свойствами.
Комментарии:
1. Чтобы добавить один вариант: определите базовый c’torкак
Base(int? id)
, вызывая GenerateId, если параметр равен null . Кроме того, определитеprivate Derived(int SomeFieldInitialValue, int? id)
выполнение всей работы и перенаправление с общедоступных c’торов на этот новый.2. Это слишком абстрактный вопрос. Можете ли вы реорганизовать базовый класс, чтобы там был только один конструктор? Например. используйте фабричный метод для создания экземпляра с сгенерированным идентификатором. Затем вы можете связать производные конструкторы классов, вызвав
this(...)
вместоbase(...)
, это сохранит всю инициализацию в одном месте.3. Согласен с Клаусом — поскольку все базовые конструкторы защищены, а класс абстрактен — объединение их в один (принятие
int?
) не повредит «общедоступному» использованию (не сделает его менее удобным), при этом устраняя вашу проблему с унаследованными классами.
Ответ №1:
Если вы можете изменить базовый класс, было бы лучше иметь там только один конструктор. Затем это может предоставить два варианта (фиксированный идентификатор или вновь сгенерированный идентификатор) следующим образом:
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 )
{
}
}
Комментарии:
1. Ох. Мне это действительно нравится. Спасибо! У меня есть малейшее беспокойство по поводу того, что это менее эффективно, чем прямой вызов разных конструкторов (я работаю в разработке игр, поэтому эффективность является огромным приоритетом), но создание объектов (и вызов конструкторов) уже признано медленным, поэтому замедление работы конструкторов не должно иметь значения 🙂