RM.
RM.

Reputation: 2043

How can I check whether something supports a generic interface?

I am using Delphi XE2. Currently I have an object based model and each model object can have multiple validators. Here is the simplified implementation of the validator generic abstract class. The concrete validator classes can override DoValidate and they do not have to cast the model object. The validator gets used using its IValidator interface.

unit ObjectBasedValidator;

interface

uses
  System.SysUtils,
  System.Generics.Collections;

type
  TModelEntity = class
  end;

type
  IValidator = interface
    procedure Validate(aEntity: TModelEntity; aResult: string);
  end;

  TValidator<T: TModelEntity> = class(TInterfacedObject, IValidator)
  private
  protected
    procedure DoValidate(aEntity: T; aResult: string); virtual; abstract;
  public
    procedure Validate(aEntity: TModelEntity; aResult: string);
  end;

implementation

{ TValidator<T> }

procedure TValidator<T>.Validate(aEntity: TModelEntity; aResult: string);
begin
  if not (aEntity is T) then
    Exit;

  DoValidate(aEntity as T, aResult);
end;

end.

Now I am trying to change the object model to interface based. So here is the updated validator unit:

unit InterfaceBasedValidator;

interface

type
  IModelEntity = interface
  end;

type
  IValidator = interface
    procedure Validate(aEntity: IModelEntity; aResult: string);
  end;

  TValidator<I: IModelEntity> = class(TInterfacedObject, IValidator)
  private
  protected
    procedure DoValidate(aEntity: I; aResult: string); virtual; abstract;
  public
    procedure Validate(aEntity: IModelEntity; aResult: string);
  end;

implementation

{ TValidator<T> }

procedure TValidator<I>.Validate(aEntity: IModelEntity; aResult: string);
begin
  // The next line does not compiles
  if not (aEntity is I) then
    Exit;

  DoValidate(aEntity as I, aResult);
end;

end.

I put a comment to the line which does not compile. Now obviously the "I" generic type would need to have a GUID defined for this to work, however there is no way to specify this requirement as a constraint.

A possible workaround could be to not to use a generic abstract class and cast the interface in the validator, but I am just wondering if someone has an idea how to do this without casting.

Upvotes: 2

Views: 381

Answers (1)

Ondrej Kelle
Ondrej Kelle

Reputation: 37221

The following seems to work:

uses
  SysUtils, TypInfo;

{ TValidator<I> }

procedure TValidator<I>.Validate(const aEntity: IModelEntity; aResult: string);
var
  intf: I;
begin
  if not Supports(aEntity, GetTypeData(TypeInfo(I))^.Guid, intf) then
    Exit;

  DoValidate(intf, aResult);
end;

Upvotes: 1

Related Questions