4

我正在编写一个基于 TDictionary 的简单依赖注入/控制系统反转,该 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

邮编内容:

  • 4 个项目都使用相同的源文件,仅在条件定义上有所不同(这就是为什么还包括 dproj 的原因)
  • 4个源文件
  • groupproj 及其 dsk 与所有 4 个项目
  • RunTestApps.cmd 运行所有 4 个项目
  • Results.txt 与我运行 RunTestApps.cmd 的输出
  • WriteUp.txt 与此问题的文本

请记住,在任何时候您都需要执行“构建所有项目”,因为所有 dcu 和 exe 都将进入源目录,否则您将面临很多错误和/或混淆,因为 exe 不是做它的名字所表明的。

4

3 回答 3

8

这正如预期的那样。正如 Uwe 指出的那样,自引用类构造函数不足以触发包含。将引用放在初始化部分就可以了,因为它在类本身之外。试图自我引用一个类以包含在内类似于试图通过拉自己的吊带将自己从一个深洞中拉出来。

于 2011-06-03T20:16:36.303 回答
7

只要您不在类本身之外的任何地方使用该类,编译器就会将此作为根本不使用的类,因此不会调用类构造函数。所以我猜你必须使用初始化部分而不是调整代码来欺骗编译器。采取务实的方法而不是教条的方法。无论如何,它会更具可读性。

于 2011-06-03T19:55:06.133 回答
2

我认为您希望类构造函数的工作方式与设计的不同。类构造函数不会在第一次创建“之前”被调用。(至少在 WIN32 delphi 中没有)。如果引用了一个类,则其类构造函数将在单元的初始化代码之前运行。

如果您对类的唯一引用恰好在所述类中,那么实际上链接到您的应用程序的代码都没有引用所述类。因此,它的类构造函数永远不会被调用。

我相信注册函数属于初始化部分。在那里拥有 register 函数将强制类构造函数也被执行。我不明白您为什么希望/要求将注册代码放入类构造函数中。

如果您想了解有关类构造函数/析构函数工作方式的更多信息,可以阅读 Allen Bauer 的这篇非常好的文章:

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

于 2011-06-03T20:17:54.453 回答