In Delphi, how can you check if an IInterface reference implements a derived but not explicitly-supported interface? - delphi

If I have the following interfaces and a class that implements them -
IBase = Interface ['{82F1F81A-A408-448B-A194-DCED9A7E4FF7}']
End;
IDerived = Interface(IBase) ['{A0313EBE-C50D-4857-B324-8C0670C8252A}']
End;
TImplementation = Class(TInterfacedObject, IDerived)
End;
The following code prints 'Bad!' -
Procedure Test;
Var
A : IDerived;
Begin
A := TImplementation.Create As IDerived;
If Supports (A, IBase) Then
WriteLn ('Good!')
Else
WriteLn ('Bad!');
End;
This is a little annoying but understandable. Supports can't cast to IBase because IBase is not in the list of GUIDs that TImplementation supports. It can be fixed by changing the declaration to -
TImplementation = Class(TInterfacedObject, IDerived, IBase)
Yet even without doing that I already know that A implements IBase because A is an IDerived and an IDerived is an IBase. So if I leave out the check I can cast A and everything will be fine -
Procedure Test;
Var
A : IDerived;
B : IBase;
Begin
A := TImplementation.Create As IDerived;
B := IBase(A);
//Can now successfully call any of B's methods
End;
But we come across a problem when we start putting IBases into a generic container - TInterfaceList for example. It can only hold IInterfaces so we have to do some casting.
Procedure Test2;
Var
A : IDerived;
B : IBase;
List : TInterfaceList;
Begin
A := TImplementation.Create As IDerived;
B := IBase(A);
List := TInterfaceList.Create;
List.Add(IInterface(B));
Assert (Supports (List[0], IBase)); //This assertion fails
IBase(List[0]).DoWhatever; //Assuming I declared DoWhatever in IBase, this works fine, but it is not type-safe
List.Free;
End;
I would very much like to have some sort of assertion to catch any mismatched types - this sort of thing can be done with objects using the Is operator, but that doesn't work for interfaces. For various reasons, I don't want to explicitly add IBase to the list of supported interfaces. Is there any way I can write TImplementation and the assertion in such a way that it will evaluate to true iff hard-casting IBase(List[0]) is a safe thing to do?
Edit:
As it came up in the one of the answers, I'm adding the two major reasons I do not want to add IBase to the list of interfaces that TImplementation implements.
Firstly, it doesn't actually solve the problem. If, in Test2, the expression:
Supports (List[0], IBase)
returns true, this does not mean it is safe to perform a hard-cast. QueryInterface may return a different pointer to satisfy the requested interface. For example, if TImplementation explicitly implements both IBase and IDerived (and IInterface), then the assertion will pass successfully:
Assert (Supports (List[0], IBase)); //Passes, List[0] does implement IBase
But imagine that somebody mistakenly adds an item to the list as an IInterface
List.Add(Item As IInterface);
The assertion still passes - the item still implements IBase, but the reference added to the list is an IInterface only - hard-casting it to an IBase would not produce anything sensible, so the assertion isn't sufficient in checking whether the following hard-cast is safe. The only way that's guaranteed to work would be to use an as-cast or supports:
(List[0] As IBase).DoWhatever;
But this is a frustrating performance cost, as it is intended to be the responsibility of the code adding items to the list to ensure they are of the type IBase - we should be able to assume this (hence the assertion to catch if this assumption is false). The assertion isn't even necessary, except to catch later mistakes if anyone changes some of the types. The original code this problem comes from is also fairly performance critical, so a performance cost that achieves little (it still only catches mismatched types at run-time, but without the possibility to compile a faster release build) is something I'd rather avoid.
The second reason is I want to be able to compare references for equality, but this can't be done if the same implementation object is held by different references with different VMT offsets.
Edit 2: Expanded the above edit with an example.
Edit 3: Note: The question is how can I formulate the assertion so that the hard-cast is safe iff the assertion passes, not how to avoid the hard-cast. There are ways to do the hard-cast step differently, or to avoid it completely, but if there is a runtime performance cost, I can't use them. I want all the cost of checking within the assertion so that it can be compiled out later.
Having said that, if someone can avoid the problem altogether with no performance cost and no type-checking danger that would be great!

One thing you can do is stop type-casting interfaces. You don't need to do it to go from IDerived to IBase, and you don't need it to go from IBase to IUnknown, either. Any reference to an IDerived is an IBase already, so you can call IBase methods even without type-casting. If you do less type-casting, you let the compiler do more work for you and catch things that aren't sound.
Your stated goal is to be able to check that the thing you're getting out of your list really is an IBase reference. Adding IBase as an implemented interface would allow you to achieve that goal easily. In that light, your "two major reasons" for not doing that don't hold any water.
"I want to be able to compare references for equality": No problem. COM requires that if you call QueryInterface twice with the same GUID on the same object, you get the same interface pointer both times. If you have two arbitrary interface references, and you as-cast them both to IBase, then the results will have the same pointer value if and only if they are backed by the same object.
Since you seem to want your list to only contain IBase values, and you don't have Delphi 2009 where a generic TInterfaceList<IBase> would be helpful, you can discipline yourself to always explicitly add IBase values to the list, never values of any descendant type. Whenever you add an item to the list, use code like this:
List.Add(Item as IBase);
That way, any duplicates in the list are easy to detect, and your "hard casts" are assured to work.
"It doesn't actually solve the problem": But it does, given the rule above.
Assert(Supports(List[i], IBase));
When the object explicitly implements all its interfaces, you can check for things like that. And if you've added items to the list like I described above, it's safe to disable the assertion. Enabling the assertion lets you detect when someone has changed code elsewhere in your program to add an item to the list incorrectly. Running your unit tests frequently will let you detect the problem very soon after it's introduced, too.
With the above points in mind, you can check that anything that was added to the list was added correctly with this code:
var
AssertionItem: IBase;
Assert(Supports(List[i], IBase, AssertionItem)
and (AssertionItem = List[i]));
// I don't recall whether the compiler accepts comparing an IBase
// value (AssertionItem) to an IUnknown value (List[i]). If the
// compiler complains, then simply change the declaration to
// IUnknown instead; the Supports function won't notice.
If the assertion fails, then either you added something to the list that doesn't support IBase at all, or the specific interface reference you added for some object cannot serve as the IBase reference. If the assertion passes, then you know that List[i] will give you a valid IBase value.
Note that the value added to the list doesn't need to be an IBase value explicitly. Given your type declarations above, this is safe:
var
A: IDerived;
begin
A := TImplementation.Create;
List.Add(A);
end;
That's safe because the interfaces implemented by TImplementation form an inheritance tree that degenerates to a simple list. There are no branches where two interfaces don't inherit from each other but have a common ancestor. If there were two decendants of IBase, and TImplementation implemented them both, the above code wouldn't be valid because the IBase reference held in A wouldn't necessarily be the "canonical" IBase reference for that object. The assertion would detect that problem, and you'd need to add it with List.Add(A as IBase) instead.
When you disable assertions, the cost of getting the types right is paid only while adding to the list, not while reading from the list. I named the variable AssertionItem to discourage you from using that variable elsewhere in the procedure; it's there only to support the assertion, and it won't have a valid value once assertions are disabled.

You are right in your examination and as far as I can tell there is really no direct solution to the problem you've encountered. The reasons lies in the nature of inheritance among interfaces, which has only a vague resemblance of inheritance among classes.
An inherited interfaces is a brand new interface, that has some methods in common with the interface it inherits from, but no direct connection. So by choosing not to implement the base class interface, you are making a specific assumption that the compiled program will follow: TImplementation does not implement IBase.
I think "interface inheritance" is somewhat of a misnomer, interface extension makes more sense! A common practice is to have a base class implementing the base interface, and than derived classes implementing the extended interfaces, but in case you want a separate class that implements both simply list those interfaces. It there a specific reason you want to avoid using:
TImplementation = Class(TInterfacedObject, IDerived, IBase)
or just you don't like it?
Further Comment
You should never, even hard type cast an interface. When you do "as" on an interface it will adjust the object vtable pointers in the right way... if you do a hard cast (and have methods to call) you code can easily crash. My impression is that you are treating interfaces like objects (using inheritance and casts in the same way) while their internal working is really different!

In Test2;
You shound't retype IDerived as IBase by IBase(A) but with:
Supports(A, IBase, B);
And adding to list can be just:
List.Add(B);

Related

How to pass a list of objects to a function, which expects a list of objects which implement an interface?

tl;dr:
Trying to pass a list of objects to a function, which expects list of objects implementing an interface. Said objects implement that interface. Compiler will not allow it.
Looking for an alternative workaround or my mistake.
The setup
I did not use the actual code, and that should not matter IMO. Seems like a conceptional problem to me.
The class TObjectWithInterface implements the interface ISomeInterface and extends the class TCustomInterfacedObject, which simply works around the reference counting of IInterface as given in the documentation.
TObjectWithInterface = class(TCustomInterfacedObject, ISomeInterface)
If have a procedure which takes a list of objects implementing that interface:
procedure SomeFunction(List: TList<ISomeInterface>)
The issue
Inside of a function of TObjectWithInterface, I try to call that function, using a list of objects of TObjectWithInterface:
procedure TObjectWithInterface.DoTheStuff(ListOfObjects: TList<TObjectWithInterface>)
begin
// ...
SomeFunction(ListOfObjects); // <-- Compiler error: Incompatible types
// ...
end;
The compiler tells me the following:
E2010: Incompatible types: 'System.Generics.Collections.TList' and 'System.Generics.Collections.TList'
The dumb workaround
I really dislike my current workaround, which consists of creating a new list and type casting each TObjectWithInterface to ISomeInterface:
procedure TObjectWithInterface.DoTheStuff(ListOfObjects: TList<TObjectWithInterface>)
var
ListOfInterfaceObjects: TList<ISomeInterface>;
begin
// ...
ListOfInterfaceObjects := TList<ISomeInterface>.Create;
for var Object in ListOfObjects do
ListOfInterfaceObjects.Add(Objects as ISomeInterface);
SomeFunction(ListOfInterfaceObjects)
// ...
end;
This seems very hacky to me. I may have done something stupid, or do not understand something correctly, as this is the first time, that I am trying to use Interfaces in Delphi. Please don't be mad.
Either way, I hope someone can point out my mistake or, if this is a language limitation, has an alternative workaround.
Your "workaround" to copy the list of objects into a separate list of interfaces is actually the correct and proper solution. You can't use a TList<X> where a TList<Y> is expected, they are different and unrelated types. Just because you are disabling reference counting on the interfaces doesn't change the memory layout of the objects in relation to their interfaces, so you still have to pass around proper memory pointers, which means performing necessary conversions from one to the other.

Is there any way to dynamically cast the item type of a generics collection in Delphi?

Unlike the case with common objects, it is impossible to directly assign generics of different related types in Delphi as follows:
Possible (normal objects):
var
var_1 : TObject;
var_2 : MyTObjectSubClass;
var_1 := var_2; //Works
Not possible (generics):
var
var_1 : TList<TObject>;
var_2 : TList<MyTObjectSubClass>;
var_1 := var_2; //Does not compile
It is possible to use casts to accomplish this though, as follows:
var
var_1 : TList<TObject>;
var_2 : TList<MyTObjectSubClass>;
var_1 := TList<TObject>(var_2); //Works
This creates the need to be able to dynamically cast generics (i.e. to dynamically parameterize their generic type specification) somehow, but I have not been able to find a way to do this, so my question is: Is this in any way possible?
I'm indeed aware of the covariance/contravariance problems related to this, but in some cases it would indeed both be useful and "correct" to do such a thing.
One example of such a situation is the current code I'm writing for generic streaming of Delphi objects over a TStream, where the receiving end knows the exact type of the object that is incoming over the stream, e.g. TList<MyTObjectSubClass>. This type information is extracted by means of RTTI though (from a provided target variable to which the loaded object should be written), so I cannot explicitly mention the exact generics type in my stream-loading code in advance, but rather have to detect it by means of RTTI (which is possible, although somewhat hacky) and then write it to a target variable that I only at that run-time point will know the exact type of.
Thus, the load-object-from-stream code has to be fully generic, and thus, it would need to dynamically cast an existing TList<TObject> variable (which is defined explicitly in the code) to the exact type of TList<MyTObjectSubClass> (which I at that point have just learned about, through the use of RTTI), in order to be able to pass this object loaded from the stream to its final destination variable.
So again, is there ANY way whatsoever to accomplish this, or is it on the contrary actually completely impossible to assign to a not-in-advance-known generics collections using generic code (i.e. code that does not explicitly have some kind of "if [type of xxx is TList<TMyObject1>] then ... else if [type of xxx is TList<TMyObject2>] then ... else ..." test, containing explicit mentions of every single generics type that should be supported by it)?
PS.
The generics type of the stream-loaded object obviously already exists somewhere in the program (since it is concluded by means of RTTI on the target variable that the stream-loaded object should be written to), so I'm not asking about full run-time dynamic creation of generics types, but rather just about how to be able to dynamically pick the right one of those generics types already defined at compile-time in the program, and then cast a variable to that type.
EDIT:
By request from #RemyLebeau , here comes some more example code from my application, from its stream-loading function:
var
source_stream : TStream;
field_to_process : TRttiField;
field_type : TRttiType;
loaded_value : TValue;
temp_int : integer;
//...
//The fields of any object given to the streaming function are
//enumerated and sorted here
//...
//Then, for each field (provided in field_to_process),
//the following is done:
case field_to_process.FieldType.TypeKind of
//...
tkInteger:
begin
source_stream.ReadBufferData(temp_int);
loaded_value := TValue.From(temp_int);
end;
tkString,
tkLString,
tkWString,
tkUString:
begin
source_stream.ReadBufferData(noof_raw_bytes_in_string_data);
SetLength(raw_byte_buf, noof_raw_bytes_in_string_data + 4);
source_stream.ReadBuffer(raw_byte_buf[0], noof_raw_bytes_in_string_data);
temp_str := used_string_encoding.GetString(raw_byte_buf, 0, noof_raw_bytes_in_string_data);
loaded_value := TValue.From(temp_str);
end;
tkClass:
begin
is_generics_collection_containing_TObject_descendants := <does some hacky detection here>; //Thanks Remy :-)
if is_generics_collection_containing_TObject_descendants then
begin
<magic code goes here that loads data from the stream into the currently processed field, whose type has been detected to be of some specific generics collection type>
end;
end;
//...
end;
field_to_process.SetValue(self, loaded_value);
That should hopefully give a somewhat better overview of my problem. The superfluous code for strings and integers are just for context, by showing how some simple types are handled.
For more info about the (necessarily) "hacky detection" mentioned in the code, please see this question. After doing that, I will know the exact type of the generics collection and its subitems, for example TList<TSomeTObjectDescendant>.
So, as you hopefully can see now, the question is about the <magic code goes here that loads data from the stream into the currently processed field, whose type has been detected to be of some specific generics collection type> part. How can it be implemented?
NOTE: My problem is not to understand how to serialize/deserialize contents of an enumerable through a stream (which can of course be done by simply iterating over the items in the enumerable and then recursing the stream saving/loading code for each of them, where the number of items is given first of all in the stream). The problem is rather how to create generic code that will be able to recreate/populate any kind of generics collection of TObject descentants, whose type you only get to know at runtime, and then to finally get this into the object field that was originally enumerated by RTTI at the beginning of the stream-loading code. As an example, assume that the processed field has the type TList<TSomeTObjectDescendant>, and that you can easily load its subobjects from the stream using a call like function load_list_TSomeTObjectDescendant_subitems(input_stream : TStream) : array of TSomeTObjectDescendant. How could I then get these subitems into the TList<TSomeTObjectDescendant> field?
Type-casts and variable declarations are parsed at compile-time (though is and as casts are executed at runtime based on compiler-provided RTTI). The type being casted to, and the type of the variable being assigned to, must be known to the compiler. So what you are asking for is simply not possible with Generics. Not the way you have described it, anyway.

Casting TList<T:class> to TList<W:class>

I have a list of type TList<TForm>. I need to cast it and use it as TList<TObject> like this:
procedure mainForm.testCast;
var
listT: TList<TForm>;
listW: TList<TObject>;
obj: TObject;
begin
listT := TList<TForm>.create;
listT.add(form1);
listT.add(form2);
listW := TList<TObject>(listT); // Casting is OK
// This works, but is this fine?
for obj in listW do
memo1.lines.add(obj.className);
end;
The sample works as expected, but is it ok to cast like this between generic lists? Will this cause some data structure corruption etc?
I use it only for looping (DoGetEnumerator) purposes and some string checks i.e. I'll not add/remove items.
The real function is little more complicated. It gets reference to listT using RTTI in a TValue.
The main goal is not to link FMX.Forms in my unit.
Update:
Why are TGeneric<Base> and TGeneric<Descendant> incompatible types?
Well, your code will work, but it somewhat dubious in my view. Simply put the cast is not legal because
TList<TForm>.InheritsFrom(TList<TObject>)
is false. So a TList<TForm> object is not a TList<TObject>. If it were, then the cast would not be needed.
That this is so is because Delphi's generic types are invariant. More details can be found here:
Why is a class implementing an interface not compatible with the interface type when used in generics?
If you have any difficulty understanding why the designers made generic types invariant, consider for a moment the effect of writing listW.Add(TObject.Create) in your code. Think what it means to the true underlying object of type TList<TForm>.
So the language promises you nothing. You are venturing outside its guarantees. It so happens that the implementation of these two un-related types is compatible enough for your code to work. But that is really just an accident of implementation.
Since you are already using RTTI, then I suggest that you iterate over the list with RTTI. You can call GetEnumerator and so on using RTTI. That way you will call the actual methods of the object.

Delphi: determine actual type of a generic?

Is there any way to determine the type of a variable passed as an argument to a method? Consider the class:
TSomeClass = class
procedure AddToList<T: TDataType; U: TListClass<T>>(Element: T; List: U);
end;
with the method implementation
procedure TSomeClass.AddToList<T, U>(Element: T; List: U);
begin
if Element is TInt then
List.AddElement(TInt.Create(XXX))
else if Element is TString then
List.AddElement(TString.Create(YYY));
end;
where TInt.Create() and TString.Create() have different sets of arguments, yet, they both inherit from TDataType.
Now, I know the is-operator can't be used like this, but is there a legal alternative that does what I'm asking here?
Not being able to use the is operator here is a known issue, but there's a pretty simple workaround.
if TObject(Element) is TInt then
List.AddElement(TInt.Create(XXX))
Also, since the type of a generic is part of the class and is known at compile-time, you might be better off restructuring your code. Make two different generic classes, one of which accepts a TInt as its <T> parameter, and the other of which accepts a TString. Put the type-specific functionality into them at that level, and have them descend from a common ancestor for shared functionality.
This question I asked some time ago
Conditional behaviour based on concrete type for generic class
might be of interest, especially if you want to use not only TObject descendants but also primitive types in your conditionals.

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