嗨,我有一个Delphi构造函数问题。我想要一个带有我自己的类参数的Constuctor。我有两个表单..在第一个表单中我的类对象获取信息,而不是我调用第二个表单。通过第二种形式,我调用构造函数并给他我的类对象。
这里的代码:TForm2
...
constructor TForm2.create(bluetoothconfiguration : TAndroidBluetooth);
begin
inherited create;
bt := bluetoothconfiguration;
end;
...
这里我调用构造函数:TForm1
...
procedure TForm1.lv_devicesItemClick(const Sender: TObject;
const AItem: TListViewItem);
var
form : TForm2;
begin
bt.CreateConnectionToBluetoothDevice(AItem); //Stellt eine Verbindung mit einem Gerät her
form := TForm2.Create(bt); //bt is the class object with the information
form.Show();
end;
...
我想发送第二个表单我的对象:(
答案 0 :(得分:4)
您正在调用的继承构造函数希望传递一个owner参数。你不是那样做的。
您应该将一个owner参数添加到构造函数中,并将其传递给继承的构造函数:
constructor TForm2.Create(AOwner: TComponent; Config: TAndroidBluetooth);
begin
inherited Create(AOwner);
....
end;
然后在创建表单时传递所有者:
form := TForm2.Create(Self, bt);
也许您可能希望使用其他所有者,我无法从这里说出来。
最后一句忠告。在询问有关编译错误的问题时,请始终在问题中包含编译错误文本,以便我们知道您在询问的内容。
答案 1 :(得分:1)
你在这里指的是依赖注入(DI)的一种形式:你有一个依赖于外部对象的表单来正确操作。人们采用的显而易见的方法是在Form的构造函数中添加一个参数。但大卫在上面所说的是正确的:你不能简单地忽视“AOwner'参数 - VCL框架已经需要注入的另一个值。
这是一个经典的问题,并没有得到很多讨论,所以我想我花一点时间来详细说明。
当您创建表单时,它很少会孤立存在 - 只有主表单在大多数时间以这种方式工作。表单通常用于执行以下三种操作之一:(1)收集和返回新数据; (2)接受数据编辑并返回编辑后的数据; (3)接受一些用于做出选择的数据并以某种方式返回选择。
当您使用数据感知控件时,表单可能看起来是自给自足的;实际上,VCL框架正在(在幕后)注入所有数据感知控制机制。如果您没有在IDE中设置表格和字段数据,那么您必须在创建表单时明确注入所有这些数据,尽管大多数人都不这样做。 (我的意思是,为什么还要使用数据感知控件?)
相反,如果您创建的表单不能或不能使用数据感知控件(例如,因为他们从其他类型的没有为其定义TDataset的外部接口获取数据),那么您需要注入初始值首先是字段值,然后再获取更新后的值。
如果您正在使用一些值,这很好 - 当有人通常会将它们放在构造函数中时。但是,如果你有一大堆(想想,比方说,50),你会怎么做?你不能将它们全部放在构造函数上!
更糟糕的是,您可能需要获取更新。如果你通过构造函数注入它们,那么你需要制作它们中的一些' var'参数。这是一个非常糟糕但虽然常见的设计实践。
所以人们会做的下一件事就是思考,"哦,我将所有这些值都放入一个对象然后通过构造函数传递对象!"
但很多时候这些价值观已经存在于其他对象中,这意味着你只需要将你的作品加倍,因为现在你需要将它们传递到你的代理对象中并从你创建的代理对象中获取它们,具有讽刺意味的是,为了简化将它们传递到表格中并从表格中取出它们,
请注意,我们一直在讨论的这种方法,在DI术语中称为"构造函数注入"。显然,它有其局限性。
另一种选择是什么称为"属性注入"或者" setter injection"。
"构造函数注入的主要优点"是在对象的构造完成时,所有值都已设置并准备就绪。使用"属性/ setter注入"你构造对象,然后注入依赖值,然后显示表单。表单关闭后,您从其属性(或getter)中提取更新的值,然后在需要时释放该表单。
在Delphi中,您可能需要覆盖此类表单的OnClose方法,将Action设置为caHide,以便在窗体关闭时不释放该窗体。 (这可能是默认值;我似乎永远不会记得。)
这里的代码看起来像是一般的构造函数注入:
try
myobj := TMyObject.Create(); // an object with initial values to feed the form
myobj.intval := 10;
myobj.str := "hello world!"
myform := TMyForm.Create( self, myobj );
myform.ShowModal();
// form could be freed here if OnClose's Action has been set to caFree
// this means myform is no longer valid
// however, it may be <> NIL even if it HAS been freed!
// if not freed, then free it
myform.Free();
// now do something with the contents of myobj
if (myobj.intval <> 10) then
. . .
if (myobj.str = '') then
. . .
finally
myobj.Free();
end;
这里是属性注入的样子,假设表格中的每个字段都有setter和getter(读/写方法)的单独属性 - 在这种情况下为intval和str:
try
myform := TMyForm.Create( self );
myForm.intval := 10;
myform.str := "hello world!"
myform.ShowModal(); // ensure the form is HIDDEN when it closes, not freed!
// now do something with the field values -- myform must still be valid!
if (myform.intval <> 10) then
. . .
if (myform.str = '') then
. . .
finally
myform.Free();
end;
一般而言,属性注入可以一致地用于应用程序中的几乎所有形式。构造函数注入方法几乎总是开始在构造函数上传递一些变量并演变为使用一个对象,然后该对象开始独立生活,增加了表单的维护成本。
属性注入方法也可以使用一个对象(实际上是几个!),但在这种情况下,它是已存在的任何对象。您不需要创建新的代理对象来将数据值传入和传出表单。相反,您只需添加一些属性并从外部访问它们。
如果要在这些表单中实现某些业务逻辑和验证,可以在OnCreate / OnDestroy中进行构造函数注入,使用OnActivate / OnClose进行属性注入(但要确保OnActivate只执行一次)。
我维护了一个有> 200个表格的系统。他们在构造函数注入时使用了各种变体。我还发现了许多微妙的错误,因为从一种形式到下一种形式的一致性如此之少。最大的问题是它们将许多外部对象从其他单元传递到构造函数中,这在表单和这些外部单元之间产生了紧密耦合。我们在其中一个对象中更改了一个属性,并以六种形式出错!更糟糕的是,其他形式的错误会在稍后出现,编译器无法检测到。
在某些情况下,他们甚至使用了一个明确的TObject构造函数(即,只是一种constructor Create(); override;
方法,因为他们无法弄清楚如何使表单的构造函数初始化事情顺序正确!
甚至有些情况下代码创建了表单的实例,然后继续将值直接写入表单上的控件(TEdits,TRadioButtons等)!这是一个不可能的主要问题!
有些人认为你应该总是使用构造函数注入。虽然我在这方面没有特别的斧头,但我只能反思在实践中往往会发生什么,这是......它很危险并导致不相关单位之间过于紧密的耦合 - 在至少就如何在大型Delphi应用程序中使用Forms而言。
我已经看过很多&#34;编码标准&#34;用于项目,我不记得在使用非数据感知表单时看到一个标准化时间和如何使用构造函数与属性注入的标准。在你的下一个(或当前)项目中要考虑的事情。