Утиная машинка в Delphi 2007? - PullRequest
       70

Утиная машинка в Delphi 2007?

10 голосов
/ 29 февраля 2012

Вопрос:

Есть ли способ ввести утку с помощью Delphi 2007 (т.е. без обобщений и расширенных функций Rtti)?


Утка набирает Ресурсы для Delphi 2010 и далее:

Последнее редактирование:

Я углубился в перечисленные выше ресурсы и изучил все опубликованные ответы здесь.

В итоге я уточнил свое требование, сделав ответ на сообщение на этот вопрос.

Ответы [ 3 ]

9 голосов
/ 29 февраля 2012

С помощью 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;
6 голосов
/ 29 февраля 2012

Быстрый ответ:

Не в значительном смысле

Более длинный ответ: Согласно вики-странице «Утиная печать» определяется как:

В типизировании утки речь идет только о тех аспектах объекта, которые используются, а не о типе самого объекта.Например, на языке, не являющемся типом утки, можно создать функцию, которая принимает объект типа Duck и вызывает методы его обхода и кряка.В языке с утиной типичностью эквивалентная функция будет принимать объект любого типа и вызывать методы его обхода и шарлатанства.Если у объекта нет вызываемых методов, функция сообщает об ошибке времени выполнения.

Эквивалентный некомпилируемый код 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) вы только приблизитесь к типизации Duck для ограниченной функциональности и со значительными усилиями.Это просто не в ДНК Delphi (потому что ДНК Delphi говорит «статическая типизация»), но вы могли бы быть в состоянии получить что-то достаточно близко, с большими усилиями и только для определенной функциональности.Может быть, если вы дадите нам представление о том, какую конкретную функциональность вы хотите получить, мы сможем что-то выяснить.

4 голосов
/ 29 февраля 2012

Вот идея, которая требует создания библиотеки типов.

Используйте типы автоматизации OLE и реализуйте диспетчерские интерфейсы (двойные COM-объекты).

Теперь вы Теперь вы можете писать все, что хотите после этого типа, и мы будем выяснять во время выполнения, работает ли он или нет. Добро пожаловать в динамический набор текста.

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

Я считаю это уродливым, но другие не могут.

...