Friday, September 02, 2016

No runtime type information on interface properties

You might have tried to decorate an interface property in Delphi with a custom attribute like in this question on StackOverflow.

Unfortunately, that won't work, as I explained in my answer. No runtime type information is generated for interface properties so your attribute will have no effect, even though there's no compiler warning. You can try the code below which demonstrates it.

Of course, if you can prove me wrong or have another idea please let me know in a comment here. Thanks!

Here's the code:

program Test;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Rtti;

type
  TTestAttribute = class(TCustomAttribute)
  private
    FName: string;
  public
    constructor Create(const AName: string);
    property Name: string read FName;
  end;

  [TTestAttribute('AtInterface')]
  ITestInterface = interface(IInvokable)
    ['{92A8107E-B40E-4786-A02E-251EA0DB3937}']
    [TTestAttribute('AtMethod')]
    function GetValue: string;
    [TTestAttribute('AtMethod')]
    procedure SetValue(const AValue: string);
    [TTestAttribute('AtProperty')]
    property Value: string read GetValue write SetValue;
  end;

constructor TTestAttribute.Create(const AName: string);
begin
  inherited Create;
  FName := AName;
end;

procedure ShowAttributes(const Attributes: TArray<TCustomAttribute>);
var
  Attr: TCustomAttribute;
begin
  for Attr in Attributes do
    if Attr is TTestAttribute then
      Writeln(Format('%s(''%s'')', [Attr.ClassName, TTestAttribute(Attr).Name]))
    else
      Writeln(Attr.ClassName);
end;

procedure Main;
var
  Ctx: TRttiContext;
  AType: TRttiInterfaceType;
  Attributes: TArray<TCustomAttribute>;
  Method: TRttiMethod;
  Prop: TRttiProperty;
begin
  Ctx := TRttiContext.Create;
  AType := Ctx.GetType(TypeInfo(ITestInterface)) as TRttiInterfaceType;
  Writeln(Format('Attributes on the interface ''%s'':', [AType.Name]));
  ShowAttributes(AType.GetAttributes);
  Writeln;
  for Method in AType.GetMethods do
  begin
    Writeln(Format('Attributes on method ''%s'':', [Method.Name]));
    ShowAttributes(Method.GetAttributes);
  end;
  Writeln;
  for Prop in AType.GetProperties do
  begin
    Writeln(Format('Attributes on property ''%s'':', [Prop.Name]));
    ShowAttributes(Prop.GetAttributes);
  end;
end;

begin
  try
    Main;
    Readln;
  except
    on E: Exception do
    begin
      ExitCode := 1;
      Writeln(Format('[%s] %s', [E.ClassName, E.Message]));
    end;
  end;
end.

This produces the following output:

Attributes on the interface:
TTestAttribute('AtInterface')

Attributes on method 'GetValue':
TTestAttribute('AtMethod')
Attributes on method 'SetValue':
TTestAttribute('AtMethod')

Already the call to AType.GetProperties returns an empty array; there is no RTTI for interface properties.

No comments: