如何在创建后更改DataModule的所有者?

时间:2013-12-12 21:08:09

标签: delphi

我正在尝试将DataModule传递给表单构造函数中的表单。我还希望表单是DataModule的“Owner”,以便表单在关闭时销毁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。但是,该表单无法知道调用者是否也将数据模块传递给其他形式。

有没有办法使用构造函数来注入我的对象但仍然可以获得管理生命周期的表单?

目前正在使用Delphi XE3。

4 个答案:

答案 0 :(得分:4)

您无需更改DataModule的Owner。您可以随时销毁DataModule,即使它已分配Owner。释放DataModule时,它只会将其自身从Owner中删除,因此不会再次释放它。如果采用这种方法,您还应该添加对FreeNotification()的调用,以便在表单仍然引用时,如果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;

如果您绝对想要通过OwnerTComponent.RemoveComponent()方法更改DataModule的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;

答案 1 :(得分:3)

您希望让表单拥有数据模块。由于所有权在施工时最自然地指定,因此结论是应首先创建表格。

因此,不是将数据模块传递给表单的构造函数,而是传递允许表单调用数据模块实例化的内容。例如,您可以将函数传递给窗体的构造函数,该构造函数接受窗体作为参数并返回新建的数据模块。例如。

type
  TCreateDataModule = function(Owner: TMyForm): TMyDataModule of object;

您可以很好地在表单外创建数据模块,但没有所有者。将对象传递给表单构造函数。然后,表单可以在其析构函数中销毁数据模块。这听起来对我来说是最干净的解决方案。我发现很难看到这个选项。

我认为你已经考虑过这个选项但是拒绝了这个推理:

  

但是,该表单无法知道调用者是否也将数据模块传递给其他表单。

如果是这样,那么没有什么可以拯救你。除非使用引用计数或类似物,否则您不能拥有两个负责生命周期的对象。

答案 2 :(得分:3)

NewOwner.InsertComponent(TheComponent)可以更改组件的所有权。由于组件一次只能由一个组件拥有,因此RTL会自动从之前的所有者中删除所有权。


但是...

  

另一种选择是在关闭第二个表单时显式释放DataModule。但是,该表单无法知道调用者是否也将数据模块传递给其他形式。

如果您希望将单个DataModule传递给多个Forms,则更改DataModule的所有权不是解决方案:拥有DataModule的一个Form将无法确定它是否可以释放DataModule。因此,结论是DataModule不能由任何形式拥有,除了MainForm。 (然后我宁愿让Application对象拥有它,但这是一个品味问题。)

随后,您将需要DataModule中的引用计数机制,用于它所附加的表单。

答案 3 :(得分:0)

为什么在创建表单之前创建一个表单要使用的数据模块? 1)为什么不将数据模块单元添加到接口使用表单列表,在表单中声明数据模块类型的私有变量,并在表单的OnCreate事件中创建表单的数据模块变量...和那么你可以在OnDestroy事件中FreeAndNil数据模块。 此外,您可以进一步声明表单的数据模块变量的公共属性,您可以使用该变量从调用单元访问数据模块(即TestForm.DataModule)

2)如果您认为必须在表单之外创建数据模块,可能首先要执行大量与数据模块相关的初始化,进程等,然后将数据模块传递给表单,忘记它...并假设你正在创建的这个形式,将使用数据模块,将是一个非模态形式(这一点信息会有很大的帮助),你可以随时做1){上面}首先,在调用表单的Show方法之前,访问'TestForm.DataModule'以应用所有初始化,进程等。