Delphi: Как скрыть конструкторы предков? - PullRequest
11 голосов
/ 06 октября 2010

Обновление: урезал вопрос на более простом примере, на который нет ответа по первоначально принятому ответу

Дан следующий класс и его предок:

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, как он определен в настоящее время. «Невозможно» - правильный ответ.


Попытка ответа (не удалось)

i пробовал помечая конструкторов-потомков 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 ]

6 голосов
/ 02 ноября 2010

Невозможно сделать конструкторы, представленные в предке, недоступными для создания производного класса в 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='')

6 голосов
/ 06 октября 2010

Если я правильно помню, 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;
4 голосов
/ 08 октября 2010

Нельзя скрыть конструктор родительского класса, если он не был объявлен виртуальным или динамическим. Однако вы можете предотвратить его вызов из дочернего класса. Рассмотрим ваш пример:

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.

3 голосов
/ 06 октября 2010

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

TCellPhone = class(TComputer)
   constructor Create(PowerCord: TPowerCord=nil); deprecated;
   constructor Create(sim: TSimChip; UnlockCode: Integer); //calls inherited Create(nil)

Кроме того, используйте переопределение или вводите заново при необходимости.

1 голос
/ 30 ноября 2015

Я знаю, что это тема 5 лет, но все же она может кому-то помочь. Единственный способ скрыть конструктор предка - переименовать один из двух методов Create во что-то другое и устранить необходимость директивы overload . Это выглядит странно, но это единственный способ. По крайней мере, в старых версиях Delphi. Я не знаю, возможно ли это сейчас в версиях XE xxx.

1 голос
/ 06 октября 2010

Вы хотите снова ввести конструктор:

TiPhone = class(TCellPhone)
    constructor Create(sim: TSimChip); reintroduce;

См. TComponent.Create в исходном коде Delphi для реального примера этого.

...