Depends upon what do you mean by "keep the pointer of various forms of the application, to do operations on them later." - what kind (or kinds) of work that is? This is a question about generic software design, about decomposition, not just circular reference
or any other language-specific issue.
If all you want to do is making same work over any form - then you should derive your forms from the same BASE-FORM-CLASS and keep references to that base class, not to the specific form classes. For example if you just need to .Release
them you can just keep them all as TForm
type reference which they all are derived from. This is just a typical case of extracting common abstract interface.
TMyFormWithActions = class ( TForm ) .... end;
TMyForm1234 = class ( TMyFormWithActions ) .... end;
TMyFormABCD = class ( TMyFormWithActions ) .... end;
You can also extract the common functionality not into intermediate class, but into the MS COM interface
like Remy shown in his answer. This however is bordering with quite different memory model (ARC one) MS COM was based upon. While I do not expect TForm
have auto-destroy reference counting, I also am not totally sure it can't happen, especially in inherited and complex application. So while I do like that approach, I omitted it because sometimes in practice it might cause unexpected and premature death of objects. If you can ensure that would not happen though it might be the most clean solution.
And if you need to do DIFFERENT actions, then you can indeed not merely store references to forms themselves, but also to actions, to software snippets. Then your thread-declaring class would build a general framework to keep forms-and-procedures data cells. And then you would have extra units implementing those specific actions to be passed.
( thread-and-action interface unit ) == uses
==> ( actions for TMyFormABCD unit ) <== uses
== ( TMyFormABCD form declaration unit )
As a simplified option, you can declare those actions in the same units as forms themselves. Then you would have all form-units depend upon thread-unit, but thread-unit (remade to be generic and specific forms-agnostic) would no more depend upon any of forms-unit. Probably it might be called "Inversion of control".
See this series: http://www.uweraabe.de/Blog/2010/08/16/the-visitor-pattern-part-1/
And one more scheme to design this, which can be seen as implementing BOTH of those approaches - would be using Windows Messages.
Your "common interface", your "actions" would be represented by custom WM_xxx messages (integer consts) you would make. Then your thread would use PostMessage
API to signal those actions to the forms. And those forms - by implementing methods to deal with those messages ( or by non-implementing = ignoring those messages ) would provide those action-implementations.
See: http://www.cryer.co.uk/brian/delphi/howto_send_custom_window_message.htm
PostMessage
can be used from external thread but can not (easily) return values. SendMessage
can only be used from the main Delphi thread. Also you have to check if MyTargetForm.HandleAllocated()
before posting messages.
as
in the implementation section. This does not "reinitialize" anything, it simply refers to the existing form(s) using a reference of the correct type. – Rudy VelthuisAs
checks if the form is really the type of form you specify and throws and exception if not. It is a type safe cast. You can check the type before withis
. Also note thatas
andis
only work on objects, not on untyped pointers. – Rudy Velthuisif MyForm is TFormABCD then MyFormABCD := MyForm as TFormABCD; // now you can use MyFormABCD
. – Rudy Velthuis