我正在尝试将一个DataModule传递给表单构造函数中的一个表单。我还希望表单成为DataModule的“所有者”,这样表单在关闭时就会销毁DataModule。这就产生了两个对象在其构造函数中相互需要的问题。
创建后,我尝试设置DataModule的所有者,但这是一个只读属性。
我的第二份表格如下:
type
TSecondPopup = class(TForm)
private
FMyData: TMyData;
public
constructor Create(MyData: TMyData); reintroduce;
end;
var
SecondPopup: TSecondPopup;
implementation
{$R *.dfm}
constructor TSecondPopup.Create(MyData: TMyData);
begin
FMyData := MyData;
inherited Create(nil);
end;我的数据模块中没有特殊的代码。
在我的主表单中,当显示第二种形式时,我想这样做:
procedure TMainApp.Button1Click(Sender: TObject);
var
MyData: TMyData;
SecondPopup: TSecondPopup;
begin
MyData := TMyData.Create(nil);
SecondPopup := TSecondPopup.Create(MyData);
// Can't change owner now. It is a read only property.
// MyData.Owner := SecondPopup;
SecondPopup.Show;
end;我知道我可以将DataModule更改为表单上的一个属性。然后我可以先创建表单,然后创建设置所有者的数据模块,最后在表单上设置属性。我试图在这个项目上使用构造函数依赖注入。当我有一个共享数据模块,主表单传递给多个表单时,它一直工作得很好。在这种情况下,主表单保持数据模块,直到它存在。在这种情况下,只有一个表单需要这个数据模块,所以我希望通过设置所有者来强制它管理数据模块的生命周期。
另一个选项是在关闭第二个表单时显式释放DataModule。但是,该表单无法知道调用方是否也将数据模传递给另一个表单。
是否有一种方法可以使用构造函数注入我的对象,但仍然获得用于管理生存期的表单?
目前使用德尔菲XE3。
发布于 2013-12-12 23:24:46
使用NewOwner.InsertComponent(TheComponent)可以更改组件的所有权。而且,由于组件一次只能由一个组件拥有,RTL会自动从前一个所有者中删除所有权。
但是..。
另一个选项是在关闭第二个表单时显式释放DataModule。但是,该表单无法知道调用方是否也将数据模传递给另一个表单。
如果您想要将一个DataModule传递给多个表单,那么改变DataModule的所有权并不是解决方案:拥有DataModule的一个表单将无法影响它是否可以释放DataModule。因此得出的结论是,DataModule不能被任何形式所拥有,除非属于MainForm。(那么,我更愿意让Application拥有它,但这是一个品味问题。)
随后,您将需要在DataModule中为其附加的表单提供引用计数机制。
发布于 2013-12-12 22:38:27
您不必更改DataModule的Owner。您可以随时销毁DataModule,即使它指定了一个Owner。当DataModule被释放时,它将简单地从它的Owner中删除自己,这样就不会第二次释放它。如果采用这种方法,还应该添加对FreeNotification()的调用,以便在窗体仍在引用DataModule时,通知您是否释放了DataModule(由其Owner或其他任何人释放):
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
constructor TSecondPopup.Create(MyData: TMyData);
begin
inherited Create(nil);
FMyData := MyData;
if FMyData <> nil then
FMyData.FreeNotification(Self);
end;
destructor TSecondPopup.Destroy;
begin
if FMyData <> nil then
FMyData.RemoveFreeNotification(Self);
inherited Destroy;
end;
procedure TSecondPopup.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited Notification(AComponent, Operation);
if (Operation = opRemove) and (AComponent = FMyData) then
FMyData := nil;
end;如果您绝对希望通过TComponent.RemoveComponent()和TComponent.InsertComponent()方法更改DataModule的TComponent.RemoveComponent()和TComponent.InsertComponent(),这也是可行的:
constructor TSecondPopup.Create(MyData: TMyData);
begin
inherited Create(nil);
FMyData := MyData;
if FMyData <> nil then
begin
// InsertComponent() will call RemoveComponent() internally for you...
Self.InsertComponent(FMyData);
end;
end;发布于 2013-12-12 21:19:48
您希望让表单拥有数据模块。由于所有权是在施工时最自然地指定的,因此得出的结论是,应首先创建该表单。
因此,与其将数据模块传递给表单的构造函数,不如传递一些允许表单调用数据模块实例化的内容。例如,您可以将一个函数传递给表单的构造函数,该构造函数接受表单作为参数并返回新创建的数据模块。例如。
type
TCreateDataModule = function(Owner: TMyForm): TMyDataModule of object;您完全可以在表单之外创建数据模块,但没有所有者。将对象传递给窗体构造函数的。然后,表单可以销毁其析构函数中的数据模块。对我来说这是最干净的解决方案。我发现很难超越这一选择。
我认为你已经考虑过这个选择,但却以这样的理由拒绝了它:
但是,该表单无法知道调用方是否也将数据模块传递给另一个表单。
如果是这样,那就没有什么能救你了。除非两个对象使用引用计数或类似的方法,否则不能让两个对象同时负责整个生命周期。
https://stackoverflow.com/questions/20554287
复制相似问题