Конструктор класса не вызывается, когда регистрация класса выполняется в конструкторе этого класса - PullRequest
4 голосов
/ 03 июня 2011

Я пишу простую инъекцию / инверсию зависимостей системы управления на основе TDictionary, содержащей ссылки на абстрактные классы с соответствующими классами их разработчиков.

Мои цели:

  • Избегайте прямого создания экземпляров по типу (очевидно).
  • Включение единицы класса в dpr должно быть достаточно, чтобы зарегистрировать его и быть доступным для выбора и реализации через систему di / ioc.
  • Объявите конкретные реализующие классы только в реализации только в разделе.
  • Используйте конструкторы классов вместо разделов инициализации.

Кстати, я знаю, что использование конструкторов классов, чтобы воспользоваться умными связями, и желание, чтобы включения модуля было достаточно, чтобы сделать класс доступным, побеждают друг друга. Я хочу использовать конструкторы классов вместо разделов инициализации и по другим причинам. И я хотел бы объединить весь код инициализации / регистрации класса вместо того, чтобы разбивать его между конструктором класса и разделом инициализации.

Задача

Я хочу, чтобы регистрация класса на фабрике была в конструкторе классов. К сожалению, компилятор не считает, что класс «затронут», просто используя его тип в своем собственном конструкторе класса.

Когда я помещаю функцию регистрации в раздел инициализации, компилятор думает, что класс затронут, и вызывает конструктор класса. Но это лишает меня смысла хранить весь код инициализации класса в конструкторе класса.

Два вопроса

  • Должен ли компилятор рассмотреть использование класса в своем собственном конструкторе класса, "касающегося класса", или это слишком много, чтобы ожидать от компилятора?
  • У кого-нибудь есть какие-нибудь умные идеи о том, как я все еще могу достичь своих целей, не используя раздел инициализации?
* 1 036 ** +1037 * Пример * * тысяча тридцать-девять

Абстрактные классы, используемые в приложении:

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

Ответы [ 3 ]

8 голосов
/ 04 июня 2011

Это как и ожидалось.Как указывал Уве, конструктора классов с самообращением недостаточно для включения.Размещение ссылки в разделе инициализации сделает свое дело, поскольку оно находится вне самого класса.Попытка самостоятельно сослаться на класс для включения - это все равно, что вытащить себя из глубокой ямы, потянув за собственные подтяжки.

7 голосов
/ 03 июня 2011

Пока вы не используете класс где-либо за пределами самого класса, компилятор принимает это как класс, который вообще не используется, и, следовательно, не будет вызывать конструктор класса. Так что я думаю, что вы должны использовать раздел инициализации вместо настройки кода, чтобы обмануть компилятор. Возьмите прагматический подход вместо догматического. В любом случае это будет более читабельным.

2 голосов
/ 04 июня 2011

Я думаю, вы ожидаете, что конструктор класса будет работать не так, как это было задумано.Конструктор класса не вызывается "непосредственно перед" первым созданием.(Не в WIN32 Delphi по крайней мере).Если на класс ссылаются, его конструктор класса будет запускаться до кода инициализации модуля.

Если единственная ссылка, которая у вас есть на ваш класс, оказывается внутри указанного класса, то фактически никакого кода нетссылка на ваше приложение относится к указанному классу.Следовательно, его конструктор класса никогда не будет вызван.

Я считаю, что регистровые функции относятся к разделу инициализации.Наличие функции register заставит конструктор класса также выполняться.Я не понимаю, почему вы хотели бы / требуют, чтобы код регистра был помещен в конструктор класса.

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

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

...