11

質問:

Delphi 2007でダックタイピングを行う方法はありますか(つまり、ジェネリックスと高度なRtti機能なしで)?


Delphi 2010以降のダックタイピングリソース:

最終編集:

私は上記のリソースをさらに掘り下げ、ここに投稿されたすべての回答を調査しました。

私は自分の要件を洗練し、この質問へのフォローアップ投稿をすることになります。

4

3 に答える 3

10

ObjAuto.pasと呼び出し可能なバリアントタイプの助けを借りて、それは可能であるはずです(XEで書かれていますが、Delphi 7以下でも実行されるべきです):

unit DuckTyping;

interface

function Duck(Instance: TObject): Variant;

implementation

uses
  ObjAuto,
  SysUtils,
  TypInfo,
  Variants;

type
  TDuckVarData = packed record
    VType: TVarType;
    Reserved1, Reserved2, Reserved3: Word;
    VDuck: TObject;
    Reserved4: LongWord;
  end;

  TDuckVariantType = class(TPublishableVariantType)
  protected
    function GetInstance(const V: TVarData): TObject; override;
  public
    procedure Clear(var V: TVarData); override;
    procedure Copy(var Dest: TVarData; const Source: TVarData;
      const Indirect: Boolean); override;
    function DoFunction(var Dest: TVarData; const V: TVarData;
      const Name: string; const Arguments: TVarDataArray): Boolean; override;
  end;

var
  DuckVariantType: TDuckVariantType;

{ TDuckVariantType }

procedure TDuckVariantType.Clear(var V: TVarData);
begin
  V.VType := varEmpty;
  TDuckVarData(V).VDuck := nil;
end;

procedure TDuckVariantType.Copy(var Dest: TVarData; const Source: TVarData;
  const Indirect: Boolean);
begin
  if Indirect and VarDataIsByRef(Source) then
    VarDataCopyNoInd(Dest, Source)
  else
  begin
    with TDuckVarData(Dest) do
    begin
      VType := VarType;
      VDuck := TDuckVarData(Source).VDuck;
    end;
  end;
end;

function TDuckVariantType.DoFunction(var Dest: TVarData; const V: TVarData;
  const Name: string; const Arguments: TVarDataArray): Boolean;
var
  instance: TObject;
  methodInfo: PMethodInfoHeader;
  paramIndexes: array of Integer;
  params: array of Variant;
  i: Integer;
  ReturnValue: Variant;
begin
  instance := GetInstance(V);
  methodInfo := GetMethodInfo(instance, ShortString(Name));
  Result := Assigned(methodInfo);
  if Result then
  begin
    SetLength(paramIndexes, Length(Arguments));
    SetLength(params, Length(Arguments));
    for i := Low(Arguments) to High(Arguments) do
    begin
      paramIndexes[i] := i + 1;
      params[i] := Variant(Arguments[i]);
    end;

    ReturnValue := ObjectInvoke(instance, methodInfo, paramIndexes, params);
    if not VarIsEmpty(ReturnValue) then
      VarCopy(Variant(Dest), ReturnValue);
  end
  else
  begin
    VarClear(Variant(Dest));
  end;
end;

function TDuckVariantType.GetInstance(const V: TVarData): TObject;
begin
  Result := TDuckVarData(V).VDuck;
end;

function Duck(Instance: TObject): Variant;
begin
  TDuckVarData(Result).VType := DuckVariantType.VarType;
  TDuckVarData(Result).VDuck := Instance;
end;

initialization
  DuckVariantType := TDuckVariantType.Create;

finalization
  FreeAndNil(DuckVariantType);

end.

次のように簡単に使用できます。

type
  {$METHODINFO ON}
  TDuck = class
  public // works in XE, not sure if it needs to be published in older versions
    procedure Quack;
  end;

procedure TDuck.Quack;
begin
  ShowMessage('Quack');
end;

procedure DoSomething(D: Variant);
begin
  D.Quack;
end;

var
  d: TDuck;
begin
  d := TDuck.Create;
  try
    DoSomething(Duck(d));
  finally
    d.Free;
  end;
end;
于 2012-02-29T15:28:20.250 に答える
6

素早い回答:

意味のある方法ではありません

長い答え:ウィキページによると、「ダックタイピング」は次のように識別されます。

ダックタイピングでは、オブジェクト自体のタイプではなく、使用されるオブジェクトの側面だけに関心があります。たとえば、ダックタイピングされていない言語では、ダックタイプのオブジェクトを受け取り、そのオブジェクトのwalkメソッドとquackメソッドを呼び出す関数を作成できます。ダックタイピング言語では、同等の関数は任意のタイプのオブジェクトを受け取り、そのオブジェクトのwalkメソッドとquackメソッドを呼び出します。オブジェクトに呼び出されるメソッドがない場合、関数は実行時エラーを通知します。

同等のDelphiのコンパイル不可能なコードは次のようになります。

procedure DoSomething(D);
begin
  D.Quack;
end;

目的が果たせなくなるため、意図的にタイプを指定しませんでしたD。Delphiは静的に型付けされているため、これは機能しません。いくつかの小さな機能のためにこれが必要な場合は、次のようなものを使用InterfacesまたはRTTI取得できます。

procedure DoSomething(D:TObject);
begin
  (D as ISomeIntf).Quack;
end;

RTTIを取得できる場合:

procedure DoSomething(D:TObject);
begin
  CallQuackUsingRTTI(D);
end;

私は個人的にこのRTTIメソッドを使用して、コードがTList子孫と汎用TList<T>バリアントの両方で機能するようにリストオブジェクトを識別(および操​​作)しました。

これからのポイントは次のとおりです。Delphiの最新バージョン(ジェネリックおよび包括的なRTTI)の高度な機能を使用しても、限られた機能と多大な労力でダックタイピングに近づくことができます。これは、DelphiのDNAには含まれていません(DelphiのDNAは「静的型付け」と言っているため)が、特定の機能に対してのみ、十分に、多くの労力を費やして、何かを近づけることができる場合があります。たぶん、あなたがどんな特定の機能が欲しいかについて私たちにアイデアを与えれば、私たちは何かを理解することができるでしょう。

于 2012-02-29T11:46:52.500 に答える
4

これは、タイプライブラリを作成する必要があるアイデアです。

OLEオートメーションタイプを使用し、ディスパッチインターフェイス(デュアルCOMオブジェクト)を実装します。

これで、そのタイプの後に好きなものを書くことができます。実行時に、それが機能するかどうか、または爆発するかどうかを調べます。動的型付けへようこそ。

procedure DoSomething(D:OleVariant);
begin
  D.Quack; // Might work, might blow up.
end;

私はそれを醜いと思いますが、他の人はそうではないかもしれません。

于 2012-02-29T12:18:21.793 に答える