何かが汎用インターフェースをサポートしているかどうかを確認する方法はありますか?

私はDelphi XE2を使用しています。現在、私はオブジェクトベースのモデルを持っており、各モデルオブジェクトは複数のバリデーターを持つことができます。バリデータジェネリック抽象クラスの単純化された実装を次に示します。具体的なバリデータクラスはDoValidateをオーバーライドすることができ、モデルオブジェクトをキャストする必要はありません。バリデーターはIValidatorインターフェースを使用して使用されます。

unit ObjectBasedValidator;

interface

uses
  System.SysUtils,
  System.Generics.Collections;

type
  TModelEntity = class
  end;

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

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

implementation

{ TValidator }

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

  DoValidate(aEntity as T, aResult);
end;

end.

今私はインタフェースベースのオブジェクトモデルを変更しようとしています。更新されたバリデータユニットがここにあります:

unit InterfaceBasedValidator;

interface

type
  IModelEntity = interface
  end;

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

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

implementation

{ TValidator }

procedure TValidator.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"ジェネリック型には、これを動作させるために定義されたGUIDが必要ですが、この要件を制約として指定する方法はありません。

可能な回避策は、一般的な抽象クラスを使用せず、バリデーターでインターフェースをキャストすることですが、キャストなしでこれを行う方法を誰かが知っているかどうかは疑問です。

2
これを行うには、 GUIDを持つという制約を指定する方法が必要であると思います。
追加された 著者 David Heffernan,

1 答え

次のように動作するようです:

uses
  SysUtils, TypInfo;

{ TValidator }

procedure TValidator.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;
1
追加された
私は確かめるためにRTLコードを踏む必要がありますが、何かがあれば、null GUIDを要求することは一般的な IInterface/IUnknown インターフェイスポインタを返すと思います。
追加された 著者 Remy Lebeau,
I にGUIDがない場合の障害モードは何ですか?
追加された 著者 David Heffernan,
その場合、 intf に表示される内容だけを推測することができます。
追加された 著者 David Heffernan,
GUIDはnull({00000000-0000-0000-0000-000000000000})ですが、面白いことにSupportsはまだTrueを返します。
追加された 著者 Ondrej Kelle,