Конструктор класса не вызывается, когда регистрация класса выполняется в конструкторе этого класса
Я пишу простую инъекцию / инверсию зависимостей системы управления, основанную на TDictionary, содержащем ссылки на абстрактные классы с соответствующими классами разработчиков.
Мои цели:
- Избегайте прямой реализации по типу (очевидно).
- Включение единицы класса в dpr должно быть достаточно, чтобы зарегистрировать его и быть доступным для выбора и реализации через систему di / ioc.
- Объявите конкретные реализующие классы только в разделе реализации.
- Используйте конструкторы классов вместо разделов инициализации.
Кстати, я знаю, что использование конструкторов классов для использования умных связей и желание, чтобы включения модуля было достаточно, чтобы сделать класс доступным, побеждают друг друга. Я хочу использовать конструкторы классов вместо разделов инициализации и по другим причинам. И я хотел бы объединить весь код инициализации / регистрации класса вместо того, чтобы разбивать его между конструктором класса и разделом инициализации.
проблема
Я хочу, чтобы регистрация класса на фабрике была в конструкторе классов. К сожалению, компилятор не считает, что класс "тронут", просто используя его тип в своем собственном конструкторе классов.
Когда я помещаю функцию регистрации в раздел инициализации, компилятор думает, что класс затронут, и вызывает конструктор класса. Но это лишает меня смысла хранить весь код инициализации класса в конструкторе классов.
Два вопроса
- Должен ли компилятор рассмотреть использование класса в своем собственном конструкторе класса, "касающегося класса", или это слишком много, чтобы ожидать от компилятора?
- У кого-нибудь есть какие-нибудь умные идеи о том, как я все еще могу достичь своих целей, не используя раздел инициализации?
пример
Абстрактные классы, используемые в приложении:
TSite = class abstract (TObject)
function GetURL: string; virtual; abstract;
property URL: string read GetURL;
end;
TSites = class (TList<TSite>);
TThisApplication = class abstract (TObject)
function Sites: TSites; virtual; abstract;
end;
Конкретный реализующий класс (объявленный в разделе реализации!) Для TThisApplication
TThisApplicationConcrete = class(TThisApplication)
class constructor ClassCreate;
strict private
FSites: TSites;
function Sites: TSites; override;
end;
class constructor TThisApplicationConcrete.ClassCreate;
begin
RegisterImplementorClass(TThisApplication, TThisApplicationConcrete);
end;
function TThisApplicationConcrete.Sites: TSites;
var
SiteList: TSites;
begin
if not Assigned(FSites) then begin
SiteList := TSites.Create; // Change to use factory
//RetrieveSites(SiteList);
FSites := SiteList;
end;
Result := FSites;
end;
Функция для получения экземпляра TThisApplication:
function ThisApplication: TThisApplication;
var
ImplementorClass: TClass;
begin
ImplementorClass := GetImplementorClass(TThisApplication);
if Assigned(ImplementorClass) then begin
Result := ImplementorClass.Create as TThisApplication;
end else begin
Result := nil;
end;
end;
В настоящее время это закодировано в отдельной функции, но оно может быть перенесено на завод.
Полный пример кода
Если кто-то захочет поэкспериментировать, у меня есть полный код моих тестовых проектов, доступный по адресу: http://www.bjsoftware.com/delphistuff/stackoverdlow/classconstructors.zip
Содержимое Zip:
- Все 4 проекта используют одни и те же исходные файлы, отличающиеся только условными определениями (поэтому в них также включены dproj)
- 4 исходных файла
- Группрой и ее дск со всеми 4 проектами
- RunTestApps.cmd для запуска всех 4 проектов
- Results.txt с выводом моего запуска RunTestApps.cmd
- WriteUp.txt с текстом этого вопроса
Пожалуйста, имейте в виду, что вам всегда нужно делать "Build All Projecs", потому что все dcu и exe идут в исходный каталог, и в противном случае вы столкнетесь с множеством ошибок и / или путаницы, потому что exe не делать то, что указывает его имя.
3 ответа
Это как и ожидалось. Как указывал Уве, конструктора классов с самообращением недостаточно для включения. Размещение ссылки в разделе инициализации сделает свое дело, поскольку оно находится за пределами самого класса. Попытка самостоятельно сослаться на класс для включения - это все равно что пытаться вытащить себя из глубокой ямы, натягивая собственные подтяжки.
Пока вы не используете класс где-либо за пределами самого класса, компилятор воспринимает это как класс, который вообще не используется, и, следовательно, не будет вызывать конструктор класса. Так что я думаю, что вы должны использовать раздел инициализации вместо настройки кода, чтобы обмануть компилятор. Возьмите прагматический подход вместо догматического. В любом случае это будет более читабельным.
Я думаю, вы ожидаете, что конструктор класса будет работать не так, как это было задумано. Конструктор класса не вызывается "непосредственно перед" первым созданием. (Не в WIN32 Delphi по крайней мере). Если на класс ссылаются, его конструктор класса будет работать до кода инициализации модуля.
Если единственная ссылка, которая у вас есть на ваш класс, оказывается внутри указанного класса, то никакой код, фактически связанный с вашим приложением, не ссылается на указанный класс. Следовательно, его конструктор класса никогда не будет вызван.
Я считаю, что функции регистра принадлежат разделу инициализации. Наличие функции register заставит конструктор класса также выполняться. Я не понимаю, почему вы хотели бы / требуют, чтобы код регистра был помещен в конструктор класса.
Если вам нужна дополнительная информация о том, как работает конструктор / деструктор класса, вы можете прочитать эту довольно хорошую статью Аллена Бауэра: