Конструктор класса не вызывается, когда регистрация класса выполняется в конструкторе этого класса

Я пишу простую инъекцию / инверсию зависимостей системы управления, основанную на 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 заставит конструктор класса также выполняться. Я не понимаю, почему вы хотели бы / требуют, чтобы код регистра был помещен в конструктор класса.

Если вам нужна дополнительная информация о том, как работает конструктор / деструктор класса, вы можете прочитать эту довольно хорошую статью Аллена Бауэра:

http://blogs.embarcadero.com/abauer/2009/09/04/38899

Другие вопросы по тегам