问题:
有没有办法用Delphi 2007进行鸭子打字(即没有泛型和高级Rtti功能)?
为Delphi 2010向前打字资源:
上次修改:
我已经深入研究了上面列出的资源,并在这里研究了每个发布的答案。
我最终完善了我的要求,为这个问题做了follow up post。
答案 0 :(得分:9)
在ObjAuto.pas和invokable变体类型的帮助下,它应该是可能的(用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;
答案 1 :(得分:6)
快速回答:
更长的答案:根据wiki page“鸭子打字”由以下标识:
在鸭子打字中,人们只关心使用的对象的那些方面,而不是对象本身的类型。例如,在非鸭类型语言中,可以创建一个函数,该函数接受Duck类型的对象并调用该对象的walk和quack方法。在duck-typed语言中,等效函数将采用任何类型的对象并调用该对象的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)中具有高级功能,您也只能接受Duck打字以获得有限的功能并且需要付出巨大的努力。这根本不在Delphi的DNA中(因为Delphi的DNA表示“静态类型”),但是可能能够获得足够接近的东西,并且需要付出很多努力,并且仅针对特定功能。也许如果你让我们知道你想要什么具体的功能,我们就能找到一些东西。
答案 2 :(得分:4)
这是一个需要您创建类型库的想法。
使用OLE自动化类型,并实现Dispatch Interfaces(双COM对象)。
现在你现在你可以在那种类型之后写下你想要的任何内容,我们将在运行时发现它是否有效,或者是否会爆炸。欢迎动态打字。
procedure DoSomething(D:OleVariant);
begin
D.Quack; // Might work, might blow up.
end;
我觉得它很难看,但其他人可能不会。