Delphi interface cast using TValue
I've recently experimented extensively with interfaces and D2010 RTTI. I don't know at runtime the actual type of the interface; although I will have access to it's qualified name using a string.
Consider the following:
program rtti_sb_1;
{$APPTYPE CONSOLE}
uses
SysUtils, Rtti, TypInfo, mynamespace in 'mynamespace.pas';
var
ctx: TRttiContext;
InterfaceType: TRttiType;
Method: TRttiMethod;
ActualParentInstance: IParent;
ChildInterfaceValue: TValue;
ParentInterfaceValue: TValue;
begin
ctx := TRttiContext.Create;
// Instantiation
ActualParentInstance := TChild.Create as IParent;
{$define WORKAROUND}
{$ifdef WORKAROUND}
InterfaceType := ctx.GetType(TypeInfo(IPare开发者_如何学运维nt));
InterfaceType := ctx.GetType(TypeInfo(IChild));
{$endif}
// Fetch interface type
InterfaceType := ctx.FindType('mynamespace.IParent');
// This cast is OK and ChildMethod is executed
(ActualParentInstance as IChild).ChildMethod(100);
// Create a TValue holding the interface
TValue.Make(@ActualParentInstance, InterfaceType.Handle, ParentInterfaceValue);
InterfaceType := ctx.FindType('mynamespace.IChild');
// This cast doesn't work
if ParentInterfaceValue.TryCast(InterfaceType.Handle, ChildInterfaceValue) then begin
Method := InterfaceType.GetMethod('ChildMethod');
if (Method <> nil) then begin
Method.Invoke(ChildInterfaceValue, [100]);
end;
end;
ReadLn;
end.
The contents of mynamespace.pas
is as follows:
{$M+}
IParent = interface
['{2375F59E-D432-4D7D-8D62-768F4225FFD1}']
procedure ParentMethod(const Id: integer);
end;
{$M-}
IChild = interface(IParent)
['{6F89487E-5BB7-42FC-A760-38DA2329E0C5}']
procedure ChildMethod(const Id: integer);
end;
TParent = class(TInterfacedObject, IParent)
public
procedure ParentMethod(const Id: integer);
end;
TChild = class(TParent, IChild)
public
procedure ChildMethod(const Id: integer);
end;
For completeness, the implementation goes as
procedure TParent.ParentMethod(const Id: integer);
begin
WriteLn('ParentMethod executed. Id is ' + IntToStr(Id));
end;
procedure TChild.ChildMethod(const Id: integer);
begin
WriteLn('ChildMethod executed. Id is ' + IntToStr(Id));
end;
The reason for {$define WORKAROUND}
may be found in this post.
Question: is there any way for me to make the desired type cast using RTTI? In other words: is there a way for me to invoke IChild.ChildMethod from knowing 1) the qualified name of IChild as a string, and 2) a reference to the TChild instance as a IParent interface? (After all, the hard-coded cast works fine. Is this even possible?) Thanks!
This looks like a pretty ugly instance of lazy coding in RTTI.pas. In the ConvIntf2Intf
function that takes care of interface casts within TValue, it explicitly only checks to see if you're casting to IInterface
. Any other interface will return false automatically. It could easily extract the GUID (if your interface has one) and attempt a QueryInterface call, but it doesn't do that for whatever reason. I'd report this one to QC.
精彩评论