Jump to content
jairgza

How know interface GUID from generic anonymous function using RTTI

Recommended Posts

Hi

From TForm1 object I need to know the GUID of ITestA from field MyField.   But the field is an anonymous function that I try to use a a lazy loading logic

type
  ITestA = interface
    ['{0BE93958-EAC8-429B-A498-221B93328551}']
  end;

  TForm1 = class(TForm)
  private
    MyField:TFunc<ITestA>;
  end;

 

Using RTTI can investigate TForm1 class and "MyField" is recognized as TRttiInterfaceType but the GUID property  returns  {00000000-0000-0000-0000-000000000000}

TRttiInterfaceType(rf.FieldType).GUID.ToString

 

is there any way to achieve this ?

regards

Share this post


Link to post
1 hour ago, jairgza said:

Using RTTI can investigate TForm1 class and "MyField" is recognized as TRttiInterfaceType

Can you show how you're querying MyField?

Share this post


Link to post
procedure TForm1.Button1Click(Sender: TObject);
var
  ctx:TRttiContext;
  rf:TRttiField;
begin
  //test
  ctx := TRttiContext.Create;
  rf := ctx.GetType(Self.ClassType).GetField('MyField');
  if rf.FieldType is TRttiInterfaceType then
  begin
    Log('GUID: '+ TRttiInterfaceType(rf.FieldType).GUID.ToString );
    Log('GUID: '+ rf.FieldType.Handle.TypeData.GUID.ToString );
  end;
end;

 

Hi, this is the test code

Delphi 10.2

win10

 

Share this post


Link to post

TFunc is anonymous method, which are backed by interface. So GUID you get from field type is not for ITestA,, but for TFunc<ITestA>.

 

I don't think you can get generic part out from parameterized type. In theory TFunc has Invoke method, so if you could get information on its result type, you would have ITestA. But I couldn't get Invoke information out of RTTI.  

 

The real question here is what you are actually trying to do. Your test code is obviously just a test - you already know that TForm1 MyField works with ITestA, so if you can make small test case for your actual use case, maybe there is a way to get information you need.

 

For instance if your form would be generic, you would more easily get what you need because you would have T to work with

 

  TForm1<T> = class(TForm)
  private
    MyField:TFunc<T>;
  end;

 

  • Like 1

Share this post


Link to post
Quote

I don't think you can get generic part out from parameterized type.

There is nothing in the RTTI system for that purpose, no.  You would have to manually parse the TRttiField.FieldType.QualifiedName of the field in question (in this case, yielding 'TFunc<ITestA>') to extract what is between the brackets ('ITestA'), and then get the TRttiType of that type from TRttiContext and typecast it to TRttiInterfaceType to get its GUID.

Quote

For instance if your form would be generic, you would more easily get what you need because you would have T to work with


  TForm1<T> = class(TForm)
  private
    MyField:TFunc<T>;
  end;

Except that you can't use Generic types for DFM-streamable classes.  The DFM system doesn't handle Generic class types.

Edited by Remy Lebeau

Share this post


Link to post

 

Quote

There is nothing in the RTTI system for that purpose, no.  You would have to manually parse the TRttiField.FieldType.QualifiedName

That's what I did,  extract the string name of the interface and search in my interface list.

 

Quote

The real question here is what you are actually trying to do. 

Just experimenting with rtti and trying to do some kind of dependency injection, try to inject a generic function that actually return the object when is used (Lazy loading).

 

 

Thanks for your comments 

 

Share this post


Link to post
11 minutes ago, jairgza said:

trying to do some kind of dependency injection, try to inject a generic function that actually return the object when is used (Lazy loading).

If your experiments are not just for educational purposes try out Spring - the container can do exactly that.

Share this post


Link to post

Lots of talk not much code... 

 

Here is my test

type
    IImplementsStuff = interface(IInterface)
      ['{83B3CBFA-9854-4BFC-A7B3-A015BE6B8B0B}']
      // Interface methods
    end;



[TestFixture]
StringUtilsTests = class
    public
    [Test]
    procedure InterFaceToGuid;

end;

implementation

uses SysUtils;

procedure StringUtilsTests.InterFaceToGuid;
var
  GUIDStr: string;
begin
  GUIDStr := GetInterfaceGUID(TypeInfo(IImplementsStuff));
  Assert.AreEqual('{83B3CBFA-9854-4BFC-A7B3-A015BE6B8B0B}',GUIDStr);
end;

 

Here is my implementation:
 

function GetInterfaceGUID(const TypeInfo: PTypeInfo): string;
var
  TypeData: PTypeData;
  GUID: TGUID;
begin
  Result := '';
  if TypeInfo.Kind = tkInterface then
  begin
    TypeData := GetTypeData(TypeInfo);
    // Correctly handle the conversion from byte array to TGUID
    Move(TypeData.IntfGuid, GUID, SizeOf(TGUID));
    Result := GUIDToString(GUID);
  end
  else
    raise Exception.Create('Provided type is not an interface');
end;

Hope it helps you as well as anyone else who needs it

  • Thanks 1

Share this post


Link to post

That code only works for Delphi 10.2 and later - the proper way would be to simply write GUID := TypeData.Guid;

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×