Delphi: Как скрыть конструкторы предков?
Обновление: выпотрошен вопрос на более простом примере, на который изначально не был принят ответ
Дан следующий класс и его предок:
TComputer = class(TObject)
public
constructor Create(Teapot: string='');
end;
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
Прямо сейчас TCellPhone
имеет 3 видимых конструктора:
- Кубок: целое число
- Кубок: целое число; Чайник: строка
- Чайник: строка = ''
Что мне делать, чтобы TCellPhone
так что конструктор предка (Teapot: string = ''
) не видно, оставляя только заявленные конструкторы:
- Кубок: целое число
- Кубок: целое число; Чайник: строка
Примечание. Обычно простое действие с использованием конструктора-потомка скрывает предка:
TCellPhone = class(TComputer) public constructor Create(Cup: Integer); virtual; end;
- Кубок: целое число
И если вы хотите сохранить и конструктор предка, и потомка, вы бы пометили потомка как
overload
:TCellPhone = class(TComputer) public constructor Create(Cup: Integer); overload; virtual; end;
- Кубок: целое число
- Чайник: строка = ''
В примере кода этого вопроса Delphi ошибается overload
ключевые слова:
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
думать, что:
- я хочу перегрузить мои конструкторы предком,
- когда на самом деле я хочу перегрузить его с братом
Как скрыть конструктор предка?
Примечание. Может быть невозможно скрыть невиртуальный конструктор-предок, используя язык Delphi, как он определен в настоящее время. "Невозможно" - правильный ответ.
Попытка ответа (не удалось)
я попытался пометить конструкторов-потомков reintroduce
(возвращаясь к моему режиму случайного добавления ключевых слов, пока он не заработает):
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer); reintroduce; overload; virtual;
constructor Create(Cup: Integer; Teapot: string); reintroduce; overload; virtual;
end;
Но это не сработало, все три конструктора все еще видны.:(
Оригинальный вопрос
У меня есть объект, который происходит от класса, который имеет конструкторы не хотят видеть:
TEniac = class(TObject)
constructor Create(PowerCord: TPowerCord=nil); //calls inherited Create
TComputer = class(TEniac) ...
constructor Create(PowerCord: TPowerCord=nil); //calls inherited Create(nil)
TCellPhone = class(TComputer)
constructor Create(sim: TSimChip; UnlockCode: Integer); //calls inherited Create(nil)
TiPhone = class(TCellPhone)
constructor Create(sim: TSimChip); //calls inherited Create(sim, 0)
Примечание: это гипотетический пример. Как и в реальном мире, объекты-предки не могут быть изменены без нарушения существующего кода.
Теперь, когда кто-то использует TiPhone
я не хочу, чтобы они даже могли видеть конструктор из TEniac
:
iphone := TiPhone.Create(powerCord);
Что еще хуже: если они вызывают этот конструктор, они полностью пропускают мой конструктор, и все, что делается между ними. Неправильный конструктор довольно легко вызвать, все они видны в дополнении кода IDE и будут компилироваться:
TiPhone.Create;
и они получают совершенно недействительный объект.
я мог бы изменить TCellPhone
бросить исключение в этих конструкторах:
TCellPhone.Create(PowerCord: TPowercord)
begin
raise Exception.Create('Don''t use.');
end;
Но разработчики не поймут, что они называют неправильный конструктор, пока клиент однажды не обнаружит ошибку и не оштрафует нас на миллиарды долларов. На самом деле, я пытаюсь найти везде, где я вызываю неправильный конструктор - но я не могу понять, как заставить Delphi сказать мне!
6 ответов
Невозможно сделать конструкторы, введенные в предке, недоступными для создания производного класса в Delphi, потому что вы всегда можете сделать это:
type
TComputerClass = class of TComputer;
var
CellPhoneClass: TComputerClass = TCellPhone;
CellPhone : TCellPhone;
begin
CellPhone := CellPhoneClass.Create('FUBAR') as TCellPhone;
end;
Ничто из того, что вы могли бы сделать в коде любого производного класса, никогда не сможет помешать кому-либо вызвать конструктор TComputer.Create для создания экземпляра производного класса.
Лучшее, что вы можете сделать, это:
TComputer = class(TObject)
public
constructor Create(Teapot: string=''); virtual;
end;
TCellPhone = class(TComputer)
public
constructor Create(Teapot: string=''); overload; override;
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
В этом случае приведенный выше код, по крайней мере, будет вызывать TCellPhone.Create(Teapot: string='')
вместо TComputer.Create(Teapot: string='')
Если я правильно помню, то reintroduce
должен помочь для виртуальных методов.
Директива reintroduce подавляет предупреждения компилятора о сокрытии ранее объявленных виртуальных методов. Используйте reintroduce, если вы хотите скрыть унаследованный виртуальный метод с новым.
Чтобы ответить на ваш обновленный вопрос - я думаю, что невозможно скрыть не виртуальный конструктор с перегрузкой в непосредственно производном классе, но я успешно попробовал следующее:
TComputer = class(TObject)
public
constructor Create(Teapot: string='');
end;
TIndermediateComputer = class(TComputer)
protected
// hide the constructor
constructor Create;
end;
TCellPhone = class(TIndermediateComputer)
public
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
Вместо того, чтобы вызывать исключение "Не использовать" только в переопределенных недопустимых конструкторах, рассмотрите возможность пометить их как устаревшие в классе, где они становятся недействительными. Это должно привести к хорошим предупреждениям компилятора, когда эти недопустимые конструкторы используются ошибочно.
TCellPhone = class(TComputer)
constructor Create(PowerCord: TPowerCord=nil); deprecated;
constructor Create(sim: TSimChip; UnlockCode: Integer); //calls inherited Create(nil)
Кроме того, используйте переопределение или введите заново при необходимости.
Вы не можете скрыть конструктор родительского класса, если он не был объявлен виртуальным или динамическим. Однако вы можете запретить его вызов из дочернего класса. Рассмотрим ваш пример:
TComputer = class(TObject)
public
constructor Create(Teapot: string='');
end;
TCellPhone = class(TComputer)
public
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
TComputer.Create
всегда будет виден из TCellPhone
, Вы можете предотвратить TComputer.Create
от непреднамеренного вызова путем объявления TCellPhone.Create
с такой же подписью.
TCellPhone = class(TComputer)
public
constructor Create(Teapot: string='');
constructor Create(Cup: Integer); overload; virtual;
constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
Тогда, пока у вас нет звонка inherited
в теле TCellPhone.Create(Teapot: string='')
Вы можете предотвратить TComputer.Create
от вызова в TCellPhone
и его потомки. Следующие:
TCellphone.Create;
TCellphone.Create('MyPhone');
Разрешит к реализации TCellPhone.
Дополнительно:
TiPhone = class(TCellPhone)
constructor Create;
end;
constructor TiPhone.Create;
begin
inherited;
end;
Вызову TCellPhone.Create
и не TComputer.Create
,
Вы хотите повторно ввести конструктор:
TiPhone = class(TCellPhone)
constructor Create(sim: TSimChip); reintroduce;
Увидеть TComponent.Create
в исходном коде Delphi для реального примера этого.
Я знаю, что это тема 5 лет, но все же это может кому-то помочь. Единственный способ скрыть конструктор предка - переименовать один из двух методов Create во что-то другое и убрать директиву overload. Это выглядит странно, но это единственный способ. По крайней мере, в старых версиях Delphi. Я не знаю, возможно ли это сейчас в версиях XE xxx.