2017-06-15 15 views
1

Howdey、私はいくつかのインターフェイスを実装するためにTVirtualInterfaceを使用していますDelphiは - 仮想化されたインタフェース

の基本インターフェイスにTVirtualInterfaceをキャストすることはできません。これらの相互作用は、DB内で見つかるキーを表しています。カスタムのコードジェネレーターでインターフェース定義を生成します。例:

// Base code 

IKey = interface 
    function KeyFields : string; 
    function KeyValues : Variant; 
    function GetKeyValue(const aKeyName : string) : Variant; 
    procedure SetKeyValue(const aKeyName : string; Value : Variant); 
end; 

// Generated code 

ITable1Key = interface(IKey) 
end; 

ITable1Key1 = interface(ITable1Key) 
    procedure SetField1(const Value : string); 
    function GetField1 : string; 
    property Field1 : string read GetField1 write SetField1; 
end; 

ITable1Key2 = interface(ITable1Key) 
    procedure SetField1(const Value : string); 
    function GetField1 : string; 
    property Field1 : string read GetField1 write SetField1; 
    procedure SetField2(const Value : string); 
    function GetField2 : string; 
    property Field2 : string read GetField1 write SetField1; 
end; 

// Other generated declarations 

私はTVirtualInterfaceを使用して、1つずつ実装するのではなく、各IKeyインターフェイスを実装します。 TKey.Castは、インターフェイスサポートされていないエラーを返したものの

TKey = TVirtualInterface 
public 
    constructor Create(aType : PTypeInfo); 
    function Cast : IKey; 
end; 

TKey<T : IKey> 
public 
    constructor Create; reintroduce; 
    function Cast : T; 
end; 


constructor TKey.Create(aType : PTypeInfo) 
begin 
    inherited Create(aType, aHandlerMethod); 
end; 

function TKey.Cast; 
var 
    pInfo: PTypeInfo; 
begin 
    pInfo := TypeInfo(IKey); 
    if QueryInterface(GetTypeData(pInfo).Guid, Result) <> 0 then 
    begin 
    raise Exception.CreateFmt('Sorry, TKey is unable to cast %s to its interface ', [string(pInfo.Name)]); 
    end; 
end; 

constructor TKey<T>.Create; 
begin 
    inherited Create(TypeInfo(T)); 
end; 

function TKey<T>.Cast; 
var 
    pInfo: PTypeInfo; 
begin 
    pInfo := TypeInfo(T); 
    if QueryInterface(GetTypeData(pInfo).Guid, Result) <> 0 then 
    begin 
    raise Exception.CreateFmt('Sorry, TKey<T> is unable to cast %s to its interface ', [string(pInfo.Name)]); 
    end; 
end; 

私は、TKey.Cast法を用いたT型にTKEY仮想インターフェイスをキャストする問題がない:私のTVirtualInterfaceに

ものの、。

私はそれを望んでいた方法で働いていなかった部分がSystem.Rttiでチェック:今

function TVirtualInterface.QueryInterface(const IID: TGUID; out Obj): HResult; 
begin 
    if iid = FIID then 
    begin 
    _AddRef; 
    Pointer(Obj) := @VTable; 
    Result := S_OK; 
    end 
    else 
    Result := inherited 
end; 

を、どのように私は親であるIIDに自分自身をキャストするTVirtualInterfaceを強制することができますFIIDフィールドのインタフェース? IKeyインターフェイスのTVirtualInterfaceの別のインスタンスを作成する必要がありますか?

ありがとうございました。

答えて

2

あなたは誤って使用していますTVirtualInterfaceです。それは単なるRTTIヘルパーであり、あなたはそれをまったく派生してはなりません。あなたはTInterfacedObjectから派生しているはずです。

また、TKeyクラスの両方が、コンストラクタに渡されるPTypeInfoを無視しています。非汎用のTKey.Cast()は常にIKeyのみを照会し、子孫インタフェースは照会しません。また、一般的なTKey<T>.Castは、そのIIDを取得するために常にTのRTTIに再度クエリします。したがって、コンストラクタのPTypeInfoを取り除くと、無駄になります。

TKeyは、派生インタフェースを実際に実装していない単なる基本クラスなので、は、IKey以外のインタフェースでは常に失敗します。少なくとも、一般的なTKeyは、派生したインタフェースに問い合わせることができます。

as演算子またはSysUtils.Supports()関数を使用して1つのインターフェイスを別のインターフェイスにキャストすることができるため、Cast関数は冗長です。これらは、手動QueryInterface()を使用していない、好ましい方法です。いずれの場合においても

、あなたのインターフェイスは、その宣言でのIIDが欠落しているので、あなたはとにかくインターフェイス間にキャストすることはできません。

より、このような何か試してみてください:

// Base code 

IKey = interface 
    ['{D6D212E0-C173-468C-8267-962CFC3FECF5}'] 
    function KeyFields : string; 
    function KeyValues : Variant; 
    function GetKeyValue(const aKeyName : string) : Variant; 
    procedure SetKeyValue(const aKeyName : string; Value : Variant); 
end; 

// Generated code 

ITable1Key = interface(IKey) 
    ['{B8E44C43-7248-442C-AE1B-6B9E426372C1}'] 
end; 

ITable1Key1 = interface(ITable1Key) 
    ['{0C86ECAA-A8E7-49EB-834F-77DE62BE1D28}'] 
    procedure SetField1(const Value : string); 
    function GetField1 : string; 
    property Field1 : string read GetField1 write SetField1; 
end; 

ITable1Key2 = interface(ITable1Key) 
    ['{82226DE9-221C-4268-B971-CD72617C19C7}'] 
    procedure SetField1(const Value : string); 
    function GetField1 : string; 
    property Field1 : string read GetField1 write SetField1; 
    procedure SetField2(const Value : string); 
    function GetField2 : string; 
    property Field2 : string read GetField1 write SetField1; 
end; 

// Other generated declarations 

type 
    TKey = class(TInterfacedObject, IKey) 
    public 
    function Cast : IKey; 
    // IKey methods... 
    end; 

    TKey<T : IKey> = class(TInterfacedObject, IKey, T) 
    public 
    function Cast : T; 
    end; 

    TTable1Key = class(TKey, IKey, ITable1Key) 
    end; 

    TTable1Key1 = class(TTable1Key, IKey, ITable1Key, ITable1Key1) 
    public 
    // ITable1Key1 methods... 
    end; 

    TTable1Key2 = class(TTable1Key, IKey, ITable1Key, ITable1Key2) 
    public 
    // Table1Key2 methods... 
    end; 

// and so on ... 

function TKey.Cast: IKey; 
begin 
    if not Supports(Self, IKey, Result) then 
    raise Exception.Create('Sorry, unable to cast to IKey'); 
end; 

function TKey<T>.Cast: T; 
begin 
    if not Supports(Self, GetTypeData(TypeInfo(T)).Guid, Result) then 
    raise Exception.CreateFmt('Sorry, unable to cast to %s', [string(TypeInfo(T).Name)]); 
end; 

// other class methods as needed ... 

は、派生クラスが自分の基底クラスによって実装されるインタフェースを繰り返す必要がどのように注意してください。これはDelphiの既知の制限です。派生クラスは基本クラスインタフェースを継承しません。実際の実装が基本クラスにある場合でも、各クラスは実装するインタフェースを明示的に指定する必要があります。

+1

この「制限」は新しい問題ですか? Delphiでは、継承されたインターフェイスの実装を宣言しなければなりませんでした。ただし、クラスが派生インターフェイスと基本インターフェイスの両方を直接実装したインターフェイス実装は、その基本クラス自体が宣言したベースクラスから継承されますインタフェースの実装。唯一の「問題」は、実際にインタフェース自体の実装を宣言することなく、基本クラスがインタフェースのメソッドを実装した場合でした。私の記憶がトリックをプレイしていない限り(私は現在、テストするためにDelphiを持っていません)。 – Deltics

+0

@Deltics: "*これは制限"新しい問題?* " - それはいつもそこにあった。インタフェースのメソッドの実装は、期待どおりに通常の多態性によって基本クラスから派生クラスに継承できますが、各クラスの内部インタフェーステーブルに表示するには、派生クラス宣言ごとにインタフェースを指定する必要があります。したがって、TObject.GetInterface() (拡張子 'IInterface.QueryInterface()'、 'is'と' as'演算子、 'SysUtils。Supports()'など)はそれを見ることができます。理想的ではなく、C++にはそのような制限はありません。 –

関連する問題