Register a New Class in runtime using Delphi - delphi

Is possible create (register) a new class in runtime using delphi.
I have a Class called TMyForm, is possible create a new form derived from TMyForm but with new class type.
i want something like this
var
Myform : TMyForm;
MyFormClassBase : TFormClass;
begin
MyFormClassBase := TFormClass(RegisterMyNewClass('TMyNewClass'));//obviously RegisterMyNewClass does not exist
Myform := MyFormClassBase.Create(Application);
Myform.Show;
end;
i am using delphi 7
UPDATE 1
I dont look create a new instance of the same base class, i need create an new class type in runtime derived from another class.
UPDATE 2
Thank you very much for your interest. but the purpose is a bit complex to explain (because my bad english). I have a form that allows you to edit multiple master data tables, all these tables have the same fields code (an integer primary key) and description (a varchar field), they serve to define currencies, countries, projects, groups, etc.
as logic is the same for all these tables, so only need this form by passing as parameters the title of the table name , to manage these tables. something like this
FormCurrency:= TMyForm.Create( 'Define currencys', 'CURRENCYTABLE')
if ValidateAccess(FormCurrency) then
FormCurrency.Show
else
FormCurrency.Close;
FormGroups:= TMyForm.Create( 'Define Groups', 'GROUPSTABLE')
if ValidateAccess(FormGroups) then
FormGroups.Show
else
FormGroups.Close;
on the other hand I have a validation method (called ValidateAccess) that validates the users access to the forms using the form's class . because of this if you use the same kind of form is restricted access to all the options like "define groups","define currencys", "define countrys" (which I do not want that to happen), because that i need to pass to the ValidateAccess method a diferent class.
I cannot rewrite the ValidateAccess method because exist many diferents forms already registered in the system.
I dont want create a new form type and a new unit over and over just changing the title and the table to use.
Thanks in Advance.

I don't know if I get you right, but what I understand can be achieved in this way:
type
TCurrencyForm = class(TMyForm);
TGroupsForm = class(TMyForm);
FormCurrency:= TCurrencyForm.Create( 'Define currencys', 'CURRENCYTABLE')
if ValidateAccess(FormCurrency) then
FormCurrency.Show
else
FormCurrency.Close;
FormGroups:= TGroupsForm.Create( 'Define Groups', 'GROUPSTABLE')
if ValidateAccess(FormGroups) then
FormGroups.Show
else
FormGroups.Close;
In your ValidateAccess method (assuming the parameter is named Form) you can check something like:
if Form is TCurrencyForm then
else if Form is TGroupsForm then
If you don't have access to the new form class declarations you can use Form.ClassName instead.

It looks like Uwe managed to solve your problem. I should just state for the record that it is possible to add new class types at runtime. Classes are defined by their class reference, which is a pointer to a VMT (Virtual Method Table), and if you know how VMTs are laid out you can create one of your own. I did a session on it at CodeRage last year. Unfortunately, the audio quality sucked. :(
Of course, this isn't much use to you unless you have to create classes whose definition is not available at compile time, for example if you're using a scripting engine. When all the information you need is available at compile time, go with something like what Uwe described.

Why do you need to create a new subclass of the form? You cannot change anything about that new class to make it different from the existing class at runtime. i.e. you cannot add new methods or properties.
I suspect that you have made the mistake of thinking that one form class can have only one instance. But this is not the case. You can create as many instances of a form as you wish:
var
formA : TMyForm;
formB : TMyForm;
begin
formA := TMyForm.Create(Application);
formB := TMyForm.Create(Application);
formA.Show;
formB.Show;
end;
If this isn't what you require, you will need to provide more information about just what exactly it is you are trying to achieve.

IIUC, you can have something like this:
TmyForm = class... //your normal form
...
public
property Title: string read FTitle write SetTitle;
property FormKind: TFormKind read FFormKind write SetFormKind;
function ValidateAccess: boolean;
...
end;
Where TFormKind = (fkCurrency, fkCountry, ...);
And in your SetTitle will also set the form's caption, in your SetFormKind you will do your coressponding initialization(s) if necessary whereas in ValidateAccess you will handle (most probably in a case) the different situations according to the value of FFormKind.
And to use it:
myForm:=TmyForm.Create(Application); //since we'll free it the owner can be also 'nil'
myForm.Title:='Boo!';
myForm.Kind:=fkCurrency;
if myForm.ValidateAccess then
myForm.ShowModal; //btw your 'if' structure is a little bit 'odd' to say at least. You don't need to call Close on a form which isn't showing
myForm.Free; //get rid of it. - of course this applies if we created it. Not applicable if you use 'Show' only, of course.
However perhaps you'll find better to separate the layers and have a different class to handle the validation, according to the form's properties etc.

Delphi is a 'static' language, so you cannot create a new type (or class) at run time. You can do this in some 'dynamic' languages, like Python.
If you are trying to create a new form, populated with different controls, you can do this, but you need to create each individual control, make the form it's parent (and owner) and set it's position and caption etc.
procedure TForm1.Button1Click(ASender: TObject);
var
LForm: TForm;
LLabel: TLabel;
begin
LForm := TForm.Create(nil);
try
LForm.Width := 100;
LForm.Height := 100;
LLabel := TLabel.Create(LForm);
LLabel.Parent := LForm;
LLabel.Caption := 'Hello World!';
LForm.ShowModal;
finally
FreeAndNil(LForm);
end;
end;

I have similar problem, and find some runtime solution.
Only request is that MyForm already created in runtime.
var vOldForm,vNewForm:TObject;
begin
vOldForm:=Application.FindComponent('MyForm');
If vOldForm<>nil then
vNewForm:=TFormClass(vOldForm.ClassType).Create(Application);
If vNewForm is vOldForm.ClassType then (vNewForm as TForm).Show;
end;

Related

A better way of creating from a class parameter?

In TApplication.CreateForm (not sure if it is allowed to paste the code here. I will do so if somemone confirms) it seems a way of creating an instance of a TForm descendant by using the class of the derived form and a variable pointing to the form. Both are parameters of CreateForm;
procedure TApplication.CreateForm(InstanceClass: TComponentClass; var Reference);
Is there a better or even simpler way (maybe with less code) of doing what is done in CreateForm if I wanted to have a method which creates a derived control with only some parameters as indicators of what class it is and the variable it will be using.
EDIT: I would like to have a method that creates a control which I use in my project. The method will also do some additional code related to the control so that is the reason for the method. I do not want to duplicate that additional work and the method will be called numerous times. I can implement the code in the same way as CreateForm but was wondering if there is a way of doing the same in less or simpler code.
I want to have a method which creates a derived control with only some parameters as indicators of what class it is and the variable it will be using.
You don't need a method for that. You can write it like this:
MyForm := TMyForm.Create(Owner);
Don't be put off by all the code in Application.CreateForm. That code performs many tasks, the principle of which is to assign the Application.MainForm variable. The IDE likes to encourage you to use Application.CreateForm but in reality you only need to call it once, and that is to create the main form.
If you are dead set on making this into a method then it would look like this:
function CreateForm(FormClass: TFormClass; Owner: TComponent): TForm;
begin
Result := FormClass.Create(Owner);
end;
When calling this function you would need to cast the value returned:
MyForm := TMyForm(CreateForm(TMyForm, Owner));
or
MyForm := CreateForm(TMyForm, Owner) as TMyForm;
As an alternative you could use a generic method:
type
TFormCreator = class
public
class function CreateForm<T: TForm>(Owner: TComponent): T; static;
end;
Implement it like this:
class function TFormCreator.CreateForm<T>(Owner: TComponent): T;
begin
Result := T(TFormClass(T).Create(Owner));
end;
Call it like this:
MyForm := TFormCreator.CreateForm<TMyForm>(Owner);
Pretty ridiculous isn't it? All you want to do is instantiate a form! So, I have a strong suspicion that you have been confused by the IDE's use of Application.CreateForm and believe that there is more to instantiating forms than there really is. My instincts tell me that you are actually looking for this:
MyForm := TMyForm.Create(Owner);
AS. You can post YOUR OWN code, but with regards to code which copyrights holds someone else - that gets a bit complicated. I believe it falls under USA "Fair Use" doctrine. For example you can post a snippet of VCL source to criticize or demonstrate something, but not to copy-paste it into another project and only as little of the VCL code - as required for that "fair use" intention.
A VCL form is a component, thus it needs an owner, who would be responsible for memory management. So you can create the form in a typical TComponent creation pattern.
MyFormVariable := TMyFormClass.Create( Application );
This also adds for type safety that untyped var Reference in CreateForm denies.
Whether that way is better or worse than using Application.CreateForm is up to your tastes. Personally I prefer uniform way so when I need to create a form or a datamodule explicitly I usually go the "component" way not the "application" way.
I guess (just guess) that back in old days TApplication.CreateForm added some extra required setup that "component way" of creating forms could not do, at least not before VCL would get started by Application.Run call. Or maybe there were types of TApplication - and AFAIR there are 5 ones for different projects - that were not derived from TComponent? But anyway I think today that limitations - if ever there where any - not apply any more.

Dynamically create form by name?

Is there a way to create forms dynamically by only their names;
The concept goes like this. I have a main form, and by some user selection, a number of predefined forms must be created and docked on tabitems on a pagecontols on the main form.
I do know the names of the forms and i do know when to create each one of those, but i would like to know if a better way of creating these forms by a single procedure call, and not having all of these information in my code.
Its Delphi XE3 firemonkey, on win 7.
Thanks in advance for any help
Apparently on Firemonkey Delphi doesn't automatically register form classes to be available by name, so you'll first need to add something like this to the end of the unit that holds your form class:
unit Form10;
[ ... ]
// Right before the final "end."
initialization
RegisterFmxClasses([TForm10]);
end.
This will automatically register TForm10 so it'll be available by name. Next you can use this kind of code to create a form at Runtime by it's class name:
procedure TForm10.Button1Click(Sender: TObject);
var ObjClass: TFmxObjectClass;
NewForm: TCustomForm;
begin
ObjClass := TFmxObjectClass(GetClass(ClassName));
if ObjClass <> nil then
begin
NewForm := ObjClass.Create(Self) as TCustomForm;
if Assigned(NewForm) then
NewForm.Show;
end
end;
You can only create objects when you have a class reference for it. To get a class reference for something given its string name, call FindClass. Call the constructor on the result. You may have to type-cast the result to a different metaclass before the compiler will allow you to access the constructor you want. In the VCL, you might use TFormClass, but plain old TComponentClass will work, too, since all FireMonkey objects are descendants of TComponent; the important part is that you have access to the right constructor, and that's where the one you need is introduced.
It only works for classes that have been registered. Your form classes should be registered by Delphi automatically, but if they're not, you can call RegisterClasses manually, or RegisterFmxClasses if you need to put your classes in groups.
Delphi.About.com has a VCL demonstration.

Delphi class references... aka metaclasses... when to use them

I've read the official documentation and I understand what class references are but I fail to see when and why they are best solution compared to alternatives.
The example cited in the documentation is TCollection which can be instantiated with any descendant of TCollectionItem. The justification for using a class reference is that it allows you to invoke class methods whose type is unknown at compile time (I assume this is the compile time of TCollection). I'm just not seeing how using TCollectionItemClass as an argument is superior to using TCollectionItem. TCollection would still be able to hold any descendant of TCollectionItem and still be able to invoke any method declared in TCollectionItem. Wouldn't it?
Compare this with a generic collection. TObjectList appears to offer much the same functionality as TCollection with the added benefit of type safety. You are freed from the requirement to inherit from TCollectionItem in order to store your object type and you can make a collection as type specific as you want. And if you need to access item's members from within the collection you can use generic constraints. Other than the fact that class references are available to programmers prior to Delphi 2009 is there any other compelling reason to use them over generic containers?
The other example given in the documentation is passing a class reference to a function that acts as an object factory. In this case a factory for creating objects of type TControl. Its not really apparent but I'm assuming the TControl factory is invoking the constructor of the descendant type passed to it rather than the constructor of TControl. If this is the case then I'm starting to see at least some reason for using class references.
So I guess what I'm really trying to understand is when and where class references are most appropriate and what do they buy a developer?
MetaClasses and "class procedures"
MetaClasses are all about "class procedures". Starting with a basic class:
type
TAlgorithm = class
public
class procedure DoSomething;virtual;
end;
Because DoSomething is a class procedure we can call it without an instance of TAlgorithm (it behaves like any other global procedure). We can do this:
TAlgorithm.DoSomething; // this is perfectly valid and doesn't require an instance of TAlgorithm
Once we've got this setup we might create some alternative algorithms, all sharing some bits and pieces of the base algorithm. Like this:
type
TAlgorithm = class
protected
class procedure DoSomethingThatAllDescendentsNeedToDo;
public
class procedure DoSomething;virtual;
end;
TAlgorithmA = class(TAlgorithm)
public
class procedure DoSomething;override;
end;
TAlgorithmB = class(TAlgorithm)
public
class procedure DoSomething;override;
end;
We've now got one base class and two descendent classes. The following code is perfectly valid because we declared the methods as "class" methods:
TAlgorithm.DoSomething;
TAlgorithmA.DoSomething;
TAlgorithmB.DoSomething;
Let's introduce the class of type:
type
TAlgorithmClass = class of TAlgorithm;
procedure Test;
var X:TAlgorithmClass; // This holds a reference to the CLASS, not a instance of the CLASS!
begin
X := TAlgorithm; // Valid because TAlgorithmClass is supposed to be a "class of TAlgorithm"
X := TAlgorithmA; // Also valid because TAlgorithmA is an TAlgorithm!
X := TAlgorithmB;
end;
TAlgorithmClass is a data type that can be used like any other data type, it can be stored in a variable, passed as a parameter to a function. In other words we can do this:
procedure CrunchSomeData(Algo:TAlgorithmClass);
begin
Algo.DoSomething;
end;
CrunchSomeData(TAlgorithmA);
In this example the procedure CrunchSomeData can use any variation of the algorithm as long as it's an descendant of TAlgorithm.
Here's an example of how this behavior may be used in a real-world application: Imagine a payroll-type application, where some numbers need to be calculated according to an algorithm that's defined by Law. It's conceivable this algorithm will change in time, because the Law is some times changed. Our application needs to calculate salaries for both the current year (using the up-to-date calculator) and for other years, using older versions of the algorithm. Here's how things might look like:
// Algorithm definition
TTaxDeductionCalculator = class
public
class function ComputeTaxDeduction(Something, SomeOtherThing, ThisOtherThing):Currency;virtual;
end;
// Algorithm "factory"
function GetTaxDeductionCalculator(Year:Integer):TTaxDeductionCalculator;
begin
case Year of
2001: Result := TTaxDeductionCalculator_2001;
2006: Result := TTaxDeductionCalculator_2006;
else Result := TTaxDeductionCalculator_2010;
end;
end;
// And we'd use it from some other complex algorithm
procedure Compute;
begin
Taxes := (NetSalary - GetTaxDeductionCalculator(Year).ComputeTaxDeduction(...)) * 0.16;
end;
Virtual Constructors
A Delphi Constructor works just like a "class function"; If we have a metaclass, and the metaclass knows about an virtual constructor, we're able to create instances of descendant types. This is used by TCollection's IDE Editor to create new items when you hit the "Add" button. All TCollection needs to get this working is a MetaClass for a TCollectionItem.
Yes a Collection would still be able to hold any descendant of TCollectionItem and to invoke methods on it. BUT, it wouldn't be able to instantiate a new instance of any descendant of a TCollectionItem. Calling TCollectionItem.Create constructs an instance of TCollectionItem, whereas
private
FItemClass: TCollectionItemClass;
...
function AddItem: TCollectionItem;
begin
Result := FItemClass.Create;
end;
would construct an instance of whatever class of TCollectionItem descendant is held in FItemClass.
I haven't done much with generic containers, but I think that given a choice, I would opt for the generic container. But in either case I'd still have to use a metaclass if I wanted to have the list instantiate and do whatever else needs to be done when an item is added in the container and I do not know the exact class beforehand.
For example an observable TObjectList descendant (or generic container) could have something like:
function AddItem(aClass: TItemClass): TItem;
begin
Result := Add(aClass.Create);
FObservers.Notify(Result, cnNew);
...
end;
I guess in short the advantage/benefit of metaclasses is any method/class having only knowledge of
type
TMyThing = class(TObject)
end;
TMyThingClass = class of TMyThing;
is able to construct instances of any descendant of TMyThing whereever they may have been declared.
Generics are very useful, and I agree that TObjectList<T> is (usually) more useful than TCollection. But class references are more useful for different scenarios. They're really part of a different paradigm. For example, class references can be useful when you have a virtual method that needs to be overridden. Virtual method overrides have to have the same signature as the original, so the Generics paradigm doesn't apply here.
One place where class references are used heavily is in form streaming. View a DFM as text sometime, and you'll see that every object is referred to by name and class. (And the name is optional, actually.) When the form reader reads the first line of an object definition, it gets the name of the class. It looks it up in a lookup table and retrieves a class reference, and uses that class reference to call that class's override of TComponent.Create(AOwner: TComponent) so it can instantiate the right kind of object, and then it starts applying the properties described in the DFM. This is the sort of thing that class references buy you, and it can't be done with generics.
I also would use a metaclass whenever I need to be able to make a factory that can construct not only one hard-coded class, but any class that inherits from my base class.
Metaclasses are not the term I am familiar with in Delphi circles however. I believe we call them class references, which has a less "magic" sounding name, so it's great that you put both common monikers in your question.
A concrete example of a place I have seen this used well is in the JVCL JvDocking components where a "docking style" component provides metaclass information to the base docking component set, so that when a user drags their mouse and docks a client form to the docking host form, the "tab host" and "conjoin host" forms that show grabber bars (similar in appearance to the title bar of a regular undocked window) can be of a user-defined plug-in class, that provides a customized appearance and customized runtime functionality, on a plug-in basis.
In some of my applications I have a mechanism that connects classes to forms capable of editing instances of one or more of that classes. I have a central list where those pairs are stored: a class refernce and a form class reference. Thus when I have an instance of a class I can lookup the corresponding form class, create a form from it and let it edit the instance.
Of course this could also be implemented by having a class method returning the appropriate form class, but that would require the form class to be known by the class. My approach makes a more modular system. The form must be aware of the class, but not the other way round. This can be a key point when you cannot change the classes.

How to position a form before it shows?

Our application used to make use of a common base form that all forms were meant to inherit from. I'd like to get rid of it for a number of reasons, ranging from the need to police that everyone uses it to several annoyances relating to Delphi's VFI implementation. It turns out that the bulk of the features it offered can be done in other, more reliable ways.
The one that I am not so sure about, is automatically positioning all forms in the center of their callers. So if I open Dialog A from my main form, it should be placed over the center of the main form. And if I then open Dialog B from Dialog A, it should be placed over the center of Dialog A and so on.
We used to take care of all this by setting the base form's Position property to poOwnerFormCenter and it worked great. But how do I do this app-wide?
I thought of using Screen.OnActiveFormChange, but I think this happens each time the form receives focus. I also thought of using Application.OnModalBegin but there doesn't seem to be an obvious way to find the form at the point this is called.
Has anyone tried this?
Well, obviously form inheritance is provided to solve exactly the problem you're trying to solve. Any solution is probably going to wind up mimicking form inheritance in some way.
Could you do something as simple as globally searching your code for "= class(TForm)" and replacing the TForm class with either your existing base form or a new, simplified base form class with only the functionality you need?
Failing that, you could try to modify the original TForm class itself to have the positioning behavior you want. Obviously, modifying the supplied classes is a little on the dangerous side.
If you are not going to go with a common base form, then I would suggest placing a non-visual component on each form. That component can inject the behaviors you want into the base form. If you want to have various different behaviors on different forms then give your component a role property that defines what role that form should have, and it can then inject different characteristics based on that role.
BTW, you can also have non-visual form inheritance, which is my preferred method of creating a common base class for all forms. It also has the advantage of adding properties to the form, and then based on those properties you can change the role or behavior of the form.
Without knowing more about your application, my advice would be to add the positioning code to each form individually - the advantages of not having a base class is that it makes it easier to have certain forms that do things slightly differently, and it keeps all the logic of a form together in one place.
I normally use the FormShow event for this, using the SetBounds() procedure.
With other non-form controls you can do the same thing by overriding the CMShowing message.
I took your idea of OnModalBegin and ran with it. The following is a "Hack", but it seems to work. To test simply drag around the form and click the button.
procedure TMainForm.Button1Click(Sender: TObject);
var
mForm: TForm;
begin
mForm := TForm.create(self);
mform.width := 300;
mform.height := 300;
mForm.ShowModal;
mForm.Free;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
application.OnModalBegin := modalbegin;
end;
procedure TMainForm.FormShow(Sender: TObject);
begin
if Screen.FormCount>1 then begin
screen.forms[Screen.FormCount-1].left := round((screen.forms[Screen.FormCount-2].left + screen.forms[Screen.FormCount-2].width/2) - screen.forms[Screen.FormCount-1].width/2);
screen.forms[Screen.FormCount-1].top := round((screen.forms[Screen.FormCount-2].top + screen.forms[Screen.FormCount-2].height/2) - screen.forms[Screen.FormCount-1].height/2);
application.processmessages;
screen.forms[Screen.FormCount-1].Caption := inttostr(screen.forms[Screen.FormCount-1].top)+','+inttostr(screen.forms[Screen.FormCount-1].left);
end;
end;
procedure TMainForm.ModalBegin(Sender: TObject);
begin
if Screen.FormCount>=0 then
screen.forms[Screen.FormCount-1].OnShow := FormShow;
end;

Are Delphi interfaces inherited in subclasses

If I implement an interface on a base class will it be inherited by its sub classes, I know the functions/procedures will be, but I am more interested in whether I will be able to cast the subclass to the interface and then back to its original class.
What I am hoping I can do is pass objects of different base classes to a function, and then in the function determin there type and use them as appropriate.
Is this possible and is it the correct approach?
Update
to help clear up any confusion (or to create some more) here is what I would like to do (striped down to its core).
Interface
IMyInterFace = interface
['{B7203E50-F5CB-4755-9DB1-FB41B7B192C5}']
function MyFunction: Boolean;
end;
Base Class
type TMyObject = class(TInterfacedObject,IMyInterFace)
Sub Class
type TSubMyObject = class(TMyObject)
Another Class
type TMyOtherObject = class(TInterfacedObject,IMyInterFace)
Then the usage
procedure foo();
var obj:TSubMyObject;
begin
obj := TSubMyObject.Create();
SendInterfaceObject(obj);
end;
procedure bar();
var obj:TMyOtherObject;
begin
obj := TMyOtherObject.Create();
SendInterfaceObject(obj);
end;
procedure SendInterfaceObject(iobj:IMyInterFace);
begin
if (iobj is TSubMyObject) then
begin
//code here
end
else if (iobj is TMyOtherObject) then
begin
//code here
end;
end;
Update 2
I have updated the code abit so show what I am after better.
the //code here sections have little to do with the object that are passed to it, for example if this class is TAccounts and it was passed a TEmployee object it may pay there weekly pay but if it was a TInvoice then it would check to see if it needed paying and only pay it when the date was 2 days before the dead line.
the TEmployee/TInvoice may even come from out side classes asking for payments to be made.
this is just an example.
Yes, the interface is inherited by the subclass.
It's perfectly acceptable to cast from subclass to the interface.
However, and apologies if I'm reading your question wrong, but if "and then back to its original class" means . . .
You have Interface I, class A and class B.
A implements I, and B inherits A, you possibly can, but REALLY SHOULD NOT cast from A to B.
EDIT:
You want to go from B to I and back to B . . . but you already have a reference to B, if B is what you pass to your function, so you don't need to cast from I to B (unless were talking about a different object, then No, don't do it)
Going from I to B is the same as going from A to B, you're trying to cast up the inheritance chain, which really is something you shouldn't do. Needing to do this is a code smell, it tells you that you should try and solve this problem in a different way (possibly by redesigning you classes (e.g. adding more properties / methods to I), or just deciding that the function will only work with the sub class - working with the subclass 'B' will give you access to all the methods of A & I).
Can you edit your question and add some sample code of what you're trying to do?
EDIT 2
procedure SendInterfaceObject(iobj:IMyInterFace);
begin
if (iobj is TSubMyObject) then
begin
//code here
end;
end;
The 'If' statement in there is a bad idea, and breaks OO principals. If you need to do this then either
The interface definition is
insufficient, you might want to add a
Type property to the interface
allowing you to (if iObj.SomeProperty
= 1) then . . .)
The interface is simply not the
correct soluton to this problem, and
you should pass the reference as
TSubMyObject.
EDIT 3:
#mghie: I agree with you, what I didn't explain very well was that SomeProperty has some data that allows the function to branch there, removing the dependancy of type checking. SomeProperty shouldn't 'simply' replace the type check (e.g. by putting the class name in a property, then checking the class name) That is indeed exactly the same problem.
There is some essential difference between Subclasses that inherit the interface. This difference should be expressed by either
Exposing some item of data that can
then be used in the brach
e.g.
if(item.Color = Red) then
item.ContrastColor := Blue;
else
item.ContrastColor := Red;
Or through polymorphism e.g.
IEmployee defines a CalculatePay method, TManager and TWorker implement IEmployee, each with different logic in the CalculatePay methods.
If the intent was to do something like the first case, polymorphism could be overkill (polymorphism doesn't fix every problem).
EDIT 4
You say "the //code here sections have little to do with the object that are passed to it . . ." I'm sorry but that statement is incorrect, if you need to Pay an Employee, you need to know their 1) EmployeeCode 2) Their Salary Details 3) Their bank details etc, if you're charging an invoice you need 1) InvoiceNumber 2) Invoice Amount 3) CustomerCode to charge to etc . . . this is an ideal place for Polymorphism.
Lets say the function taking the interface checks to see if "Accounts" needs to do something with the object (e.g. Pay the employee, charge an Invoice etc). So we might call the function AccountsCheck. Inside Accounts check you will have a peice of logic specific to each sub class (to pay an employee, to charge the invoice . . .) This is an ideal candidate for Polymorphism.
On you interface (or on another interface, or as a virtual method on the sub class) Define an "AccountsCheck" method. Then each derived class gets its own implementation of Accounts check.
The code moves out of your humungous single AccountsCheck function, and into smaller functions on each sub class. This makes the code
More obvious in intent (each class
contains some logic for
AccountsCheck)
You're less likely to break SubClass
B's logic when fixing something in
AccountsCheck for C
It's easier to figure out exactly
what SubClass B's AccountsCheck logic
is, you've only to check 20 lines of
code in small AccountsCheck, not 200
in the General AccountsCheck)
There are more, "good reasons" for this, aif nyone wants to edit/post comments please do so.
If you find you need to share some logic between implementations of AccountsCheck, create some utility functions, don't reimplement the same wheel in each one.
Polymorphism is the solution to your problem.
My suggestion here would be to not cast against the class but instead cast against another interface. Change your TMyOtherObject to:
type
IOtherObjectIntf = interface
['{FD86EE29-ABCA-4D50-B32A-24A7E71486A7}']
end;
type
TMyOtherObject = class(TInterfacedObject,IMyInterFace,IOtherObjectIntf)
and then change your other routine to read:
procedure SendInterfaceObject(iobj:IMyInterFace);
begin
if Supports(iObj,IOtherObjectIntf) then
begin
//code here for TMyOtherObject
end
else
begin
//code here for other standard implementations
end;
end;
This way your "custom" code for the TMyOtherObject would also be applied to any of ITS descendants without any further custom code. The IOtherObjectIntf interface is used as nothing other than a "yep, I'm one of those" indicators which allows your code to branch properly. Sure, its laying waste to another Guid...but there are so many of them, who would notice? :)
The interface is inherited by the subclasses and you can cast the objects to the interface, but it is not safe(or recommended) to cast the interface to class. If you need to do this you are probably using interfaces the wrong way.
There seems to be some doubt on how your question is to be understood, and indeed in your comment to this answer you say that you want to "go from B to I to B".
This is indeed not recommended and only supported by using information about how interfaces are implemented on a class.
If I understand you correctly then what you want to do is to pass an interface to some method, and in that method do different things depending on what concrete class the interface was implemented by. It is however best to continue using interfaces once you start with them. You could let the interface have a method to return the implementing class, but you should not make any assumptions about what class the interface is implemented in - it costs you some of the benefits of programming against interfaces.
What you can do instead is create different interfaces, and implement some of them only in (some of) your descendant classes. Then you can use QueryInterface() or Supports() on the passed interface pointer. For your base class this will return nil, but for all descendant classes that implement the interface it will return a pointer that lets you call the methods only they have.
Edit: For example in the OmniThreadLibrary you will find:
IOmniWorker = interface
['{CA63E8C2-9B0E-4BFA-A527-31B2FCD8F413}']
function GetImplementor: TObject;
...
end;
which you could add to your interface. But again, IMHO the use of distinct interfaces is much better.
You can't cast an interface to an object directly (it is not what interfaces are intended for) but sometimes it so practical to be able to do it, that you can't resist...
If you really want to do like that, you can use the example "IOmniWorker" given by mghie directly in IMyInterFace:
IMyInterFace = interface
['{B7203E50-F5CB-4755-9DB1-FB41B7B192C5}']
function MyFunction: Boolean;
function GetImplementor: TObject;
end;
The function implementations look like that:
function TMyObject.GetImplementor: TObject;
begin
Result := Self;
end;
function TMyOtherObject.GetImplementor: TObject;
begin
Result := Self;
end;
SendInterfaceObject looks then like that:
procedure SendInterfaceObject(const iobj:IMyInterFace);
begin
if (iobj.GetImplementor is TSubMyObject) then
begin
//code here
end
else if (iobj.GetImplementor is TMyOtherObject) then
begin
//code here
end;
end;
By the way I have added a (very) small optimization: by passing iobj as "const" to the function you can avoid unnecessary reference counting.

Resources