我正在编写一个基于TDictionary的控制系统的简单依赖项注入/反转,该TDictionic持有抽象类引用及其各自的实现器类。
我的目标是:
- 避免按类型直接实例化(显然)
- 在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的输出
- 将此问题的文本写入Up.txt
请记住,在任何时候,您都需要执行"构建所有Projecs",因为所有的dcu和exe都将转到源目录,否则您将面临许多错误和/或混乱,因为exe没有按照其名称所示进行操作。
这是意料之中的。正如Uwe所指出的,自引用类构造函数不足以触发包含。将引用放在初始化部分会起到作用,因为它在类本身之外。试图自我引用一个类以供包含,就像试图通过自己的吊带把自己从一个深洞里拉出来一样。
只要你不在类本身之外的任何地方使用该类,编译器就会将其视为根本没有使用的类,因此不会调用类构造函数。所以我想你必须使用初始化部分,而不是调整代码来欺骗编译器。采取务实的方法而不是教条主义的方法。无论如何,它会更易读。
我认为你希望类构造函数的工作方式与设计的不同。类构造函数不会在第一次创建之前被调用。(至少在WIN32 delphi中没有)。如果引用了一个类,那么它的类构造函数将在单元的初始化代码之前运行。
如果您对类的唯一引用恰好在所述类内,那么没有实际链接到应用程序中的代码引用所述类。因此,它的类构造函数永远不会被调用。
我认为寄存器函数属于初始化部分。在那里拥有register函数将强制类构造函数也被执行。我不明白为什么您希望/要求将寄存器代码放入类构造函数中。
如果你想要更多关于类构造函数/析构函数工作方式的信息,你可以阅读Allen Bauer的这篇很好的文章:
http://blogs.embarcadero.com/abauer/2009/09/04/38899