我有一个Delphi单元需要保留各种形式的应用程序的指针,以便稍后对它们进行操作。
为了执行这些操作,我需要将指针强制转换为表单类型,例如。
var
ptrFrmMain: Pointer;
CurrentFrmMain: TfrmMain;
begin
CurrentFrmMain := ptrFrmMain;
CurrentFrmMain.Close();
end;
问题是该单元包含在应用程序的所有其他Delphi单元的使用中。因此,虽然我可以在接口部分声明一个简单的Pointer
类型,但我无法声明在其他单元中声明的类型(例如单元TfrmMain
的{{1}})。
我可以通过在实现部分中使用来解决这个问题,例如:
frmMain.pas
但仍然存在一个问题:我需要将变量特定于我的类实例,用于多线程目的,而不是通用的全局变量。
但是我不能把它放在我的interface
type TMyThread = class(TThread)
Public
ptrFrmMain:Pointer
...
implementation
uses frmMain
var
CurrentFrmMain: TfrmMain;
类中,因为TmyThread
没有在那里声明,我不能把它放在接口部分的使用中。
解决方案是将TfrmMain
作为局部变量放在使用它的所有过程中,然后每次都进行CurrentFrmMain
转换,但是您知道更好的解决方案吗?
非常感谢你。
答案 0 :(得分:6)
我根本不会在线程中放置一个Form指针。我会让线程保持回调函数,甚至是接口:
type
TCloseProc: procedure of object;
TMyThread = class(TThread)
public
CloseProc: TCloseProc;
...
end;
...
begin
if Assigned(CloseProc) then CloseProc();
end;
type
IMyIntf = interface(IInterface)
['{9CC7DB9E-D47F-4B7D-BBF9-6E9B80823086}']
procedure DoClose;
end;
TMyThread = class(TThread)
public
Intf: IMyIntf;
...
end;
...
begin
if Assigned(Intf) then Intf.DoClose();
end;
...
type
TfrmMain = class(TForm, IMyIntf)
public
procedure doClose;
end;
procedure TfrmMain.doClose;
begin
Close;
end;
创建线程时,将Form方法分配给那些回调,或者将Form的接口实现传递给线程:
Thread := TMyThread.Create(True);
Thread.CloseProc := frmMain.Close;
Thread.Resume;
Thread := TMyThread.Create(True);
Thread.Intf := frmMain as IMyIntf;
Thread.Resume;
无论哪种方式,线程根本不需要了解实际的表单,同时仍然满足特定于表单的功能。
答案 1 :(得分:3)
取决于你的意思"保留各种形式的应用程序的指针,以便稍后对它们进行操作。" - 那是什么样的(或种类)工作?这是关于通用软件设计的问题,关于分解,不仅仅是circular reference
或任何其他语言特定的问题。
如果您要做的只是在任何表单上进行相同的工作 - 那么您应该从相同的BASE-FORM-CLASS派生您的表单并保留对该基类的引用,而不是对特定表单类的引用。例如,如果您只需要.Release
它们,您可以将它们全部保存为TForm
类型引用,它们都是从中派生出来的。这只是提取常见抽象接口的典型案例。
TMyFormWithActions = class ( TForm ) .... end;
TMyForm1234 = class ( TMyFormWithActions ) .... end;
TMyFormABCD = class ( TMyFormWithActions ) .... end;
您还可以将常用功能提取到中间类中,但是在他的答案中显示的像Remy中的MS COM interface
。然而,这与MS COM基于的完全不同的存储器模型(ARC one)接近。虽然我不希望TForm
具有自动销毁引用计数,但我也不能完全确定它是否会发生,尤其是在继承和复杂的应用程序中。因此,虽然我喜欢这种方法,但我省略了它,因为有时在实践中它可能会导致对象的意外和过早死亡。如果你能确保不会发生这种情况,尽管它可能是最干净的解决方案。
如果您需要执行不同的操作,那么您实际上不仅可以存储对表单本身的引用,还可以存储对软件片段的操作。然后你的线程声明类将构建一个通用框架来保存表单和过程数据单元。然后你会有额外的单位来实现那些要传递的具体行动。
(线程与动作接口单元)== uses
==> (TMyFormABCD单元的操作)< == uses
==(TMyFormABCD表单声明单元)
作为简化选项,您可以使用与表单本身相同的单位声明这些操作。然后你将所有表单单元依赖于线程单元,但是线程单元(重制为通用和特定表单 - 不可知)将不再依赖于任何表单单元。可能它可能被称为"反转控制"。
参见本系列:http://www.uweraabe.de/Blog/2010/08/16/the-visitor-pattern-part-1/
另外一个设计这个的方案,可以看作是实现这些方法的两个方面 - 将使用Windows消息。
您的"常用界面",您的"操作"将由您将制作的自定义WM_xxx消息(整数consts)表示。然后,您的线程将使用PostMessage
API向表单发出这些操作的信号。而那些形式 - 通过实现处理这些消息的方法(或通过非实现=忽略这些消息)将提供这些动作实现。
请参阅:http://www.cryer.co.uk/brian/delphi/howto_send_custom_window_message.htm
PostMessage
可以从外部线程使用,但不能(轻松)返回值。 SendMessage
只能在主Delphi线程中使用。此外,您必须在发布消息之前检查是否MyTargetForm.HandleAllocated()
。