Delphi вызывает виртуальный конструктор на основе типа TObject
У меня есть объект, производный от объекта TStringList, который я называю "TAutoString". Это позволяет вам указать тип объекта при создании списка. Затем каждый раз, когда в список строк добавляется новая запись, она также создает копию объекта, связанного с этой строковой записью. Это упрощает хранение всех видов дополнительной информации вместе с каждой строкой. Например:
type TMyObject = class(TObject)
public
Cats: integer;
Dogs: integer;
Mice: integer;
end;
MO := TAutoString.Create(TMyObject);
Внутри объекта информация о классе хранится в переменной класса:
private
ObjectClass: TClass;
constructor TAutoString.Create(ObjClass: TClass);
begin
inherited Create;
ObjectClass:=ObjClass;
end;
Теперь каждый раз, когда добавляется новый элемент, он создает новый объект указанного типа:
function TAutoString.Add(const S: string): Integer;
begin
Result:=inherited Add(S);
Objects[Result]:=ObjectClass.Create;
end;
Теперь я могу добавлять или читать информацию, связанную с каждой строковой записью.
TMyObject(MO.Objects[25]).Cats := 17;
D:=TMyObject(MO.Objects[25]).Dogs;
Это отлично работает, поскольку у объекта нет конструктора. Если у объекта есть конструктор, его конструктор не будет вызываться при создании объекта, потому что конструктор для TObject не является виртуальным.
Может ли кто-нибудь придумать способ обойти эту проблему. Я видел решения, которые используют библиотеки RTTI, но это в Delphi-7, в котором нет библиотеки RTTI.
Кстати, кажется немного странным, что конструктор TObject не виртуальный. Если бы это было так, это позволило бы использовать всевозможные полезные функции, подобные той, которую я пытаюсь реализовать.
РЕДАКТИРОВАТЬ: предложение Реми ниже было просто толчком, который мне был нужен. Изначально я пробовал аналогичную стратегию, но не смог заставить ее работать. Когда мне показалось, что это не работает так, как я думал, я предположил, что должно быть что-то, чего я не понимал в виртуальных методах. Его пост подтолкнул меня взглянуть на него еще раз. Оказалось, что я отказался от директивы Override для конструктора объекта, который хотел присоединить. Теперь все работает так, как должно.
Другая проблема, которая меня беспокоила, заключалась в том, что я уже использовал Auto Strings в группе других приложений, где объект был основан на "TObject", и я не хотел возвращаться и изменять весь этот код. Я решил эту проблему, перегрузив конструкторы и установив один для объектов на основе TObject, а другой - для объектов TAutoClass:
constructor Create(ObjClass: TAutoClass); overload; virtual;
constructor Create(ObjClass: TClass); overload; virtual;
В зависимости от того, какой конструктор вызывается, класс объекта хранится по-разному в другой переменной.
private
AutoClass: TAutoClass;
ObjectClass: TClass;
Затем, когда объект построен, я проверяю, какой из них был назначен, и использую его:
procedure TAutoString.CreateClassInstance(Index: integer);
begin
if AutoClass<>nil then Objects[Index]:=AutoClass.Create
else Objects[Index]:=ObjectClass.Create
end;
Новая версия отлично работает с любым типом объекта.
2 ответа
Чтобы сделать то, что вы хотите, вам нужно будет определить базовый класс для ваших объектов списка, от которого будут производиться, а затем вы можете добавить виртуальный конструктор к этому классу. Твой
ObjectClass
член должен будет использовать этот тип класса вместо использования
TClass
.
Например:
type
TAutoStringObject = class(TObject)
public
constructor Create; virtual;
end;
TAutoStringObjectClass = class of TAutoStringObject;
TAutoString = class(TStringList)
private
ObjectClass: TAutoStringObjectClass;
public
constructor Create(ObjClass: TAutoStringObjectClass);
function Add(const S: string): Integer; override;
...
end;
...
constructor TAutoStringObject.Create;
begin
inherited Create;
end;
constructor TAutoString.Create(ObjClass: TAutoStringObjectClass);
begin
inherited Create;
ObjectClass := ObjClass;
end;
function TAutoString.Add(const S: string): Integer;
var
Obj: TAutoStringObject;
begin
Obj := ObjectClass.Create;
try
Result := inherited AddObject(S, Obj);
except
Obj.Free;
raise;
end;
end;
...
Затем вы просто настраиваете свои производные классы объектов для использования
TAutoStringObject
вместо
TObject
, например:
type
TMyObject = class(TAutoStringObject)
public
...
constructor Create; override;
end;
MO := TAutoString.Create(TMyObject);
...
И их конструктор будет вызван, как и ожидалось.
Вот подсказка для более чистого решения:
В Tobject можно добавить виртуальный конструктор.
Для этого вам нужно будет использовать так называемый «помощник класса».
Вот пример:
type
TobjectHelper = class helper for Tobject
public
constructor Create; virtual; // adds a virtual constructor to Tobject.
end;
(Моим вариантом использования была функция TryCreateObject для обнаружения ситуации с нехваткой памяти во время создания объекта, обертывает try except end и просто возвращает true / false, чтобы предотвратить блоки try except в коде и вместо этого использовать более логические управляемые операторы if)
Помощники классов были представлены (?) В Delphi 8 и более поздних версиях. Ваши требования относятся к Delphi 7, поэтому это может не сработать.
Если вы не пишете код для Windows 95 / Windows 98 / Windows XP, возможно, вам пора перейти на более новую версию Delphi, особенно версии Delphi XE для поддержки юникода, иначе вы кодируете устаревшую платформу, которая вот-вот станет устаревшей. и т.п.
Однако для Windows 95 / Windows 98 и Windows XP я считаю, что Delphi 2007 может быть полезен, я считаю, что он может компилировать код, который может работать на этих старых платформах Windows, хотя я могу ошибаться.
Более поздние версии Delphi требуют наличия определенных системных библиотек Windows, иначе сборка / скомпилированный исполняемый файл не будет работать, w95 / w98 / wxp не имеют этих dll.