gpt4 book ai didi

delphi - 如何比较包含对象函数/过程的 TFunc/TProc?

转载 作者:行者123 更新时间:2023-12-03 14:40:07 26 4
gpt4 key购买 nike

我们使用TList<TFunc<Boolean>>与一些function ... of object位于其中,现在想要 Remove()再次一些条目。但它不起作用,因为显然你根本无法比较这些 reference to ...事情可靠。

这是一些测试代码:

program Project1;

{$APPTYPE CONSOLE}

uses
Generics.Defaults,
SysUtils;

type
TFoo = class
strict private
FValue: Boolean;
public
constructor Create();
function Bar(): Boolean;
end;

{ TFoo }

function TFoo.Bar: Boolean;
begin
Result := FValue;
end;

constructor TFoo.Create;
begin
inherited;

FValue := Boolean(Random(1));
end;

function IsEqual(i1, i2: TFunc<Boolean>): Boolean;
begin
Result := TEqualityComparer<TFunc<Boolean>>.Default().Equals(i1, i2);
end;

var
s: string;
foo: TFoo;
Fkt1, Fkt2: TFunc<Boolean>;

begin
try
Foo := TFoo.Create();

WriteLn(IsEqual(Foo.Bar, Foo.Bar)); // FALSE (1)
WriteLn(IsEqual(Foo.Bar, TFoo.Create().Bar)); // FALSE (2)

Fkt1 := function(): Boolean begin Result := False; end;
Fkt2 := Fkt1;
WriteLn(IsEqual(Fkt1, Fkt2)); // TRUE (3)

Fkt2 := function(): Boolean begin Result := False; end;
WriteLn(IsEqual(Fkt1, Fkt2)); // FALSE (4)

Fkt2 := function(): Boolean begin Result := True; end;
WriteLn(IsEqual(Fkt1, Fkt2)); // FALSE (5)

FreeAndNil(Foo);
except
on E:Exception do
Writeln(E.Classname, ': ', E.Message);
end;
Readln(s);
end.

我们几乎尝试了一切,=运算符、比较指针等等。

我们甚至尝试了一些非常令人讨厌的事情,例如反复转换到 PPointer并取消引用,直到我们获得相等的值,但这当然也没有产生令人满意的结果 =)。

  • 情况 (2)、(4) 和 (5) 都可以,因为它们实际上具有不同的功能。
  • 情况 (3) 很简单,也还可以。
  • 情况 (1) 是我们想要检测的情况,但这是我们无法检测的情况。

我担心,Delphi 会悄悄创建两个不同的匿名函数,将调用转发到 Foo.Bar 。在这种情况下,我们将完全无能为力,除非我们想涉过未知内存的泥沼……好吧,我们不想。

最佳答案

您必须通过其他方式将名称或索引与它们关联起来。匿名方法没有名称并且可以捕获状态(因此它们是按实例重新创建的);没有什么简单的方法可以在不破坏封装的情况下使它们具有可比性。

你可以得到方法引用后面的对象,如果它后面确实有一个对象(不能保证这一点 - 方法引用的接口(interface)是按照 COM 语义实现的,它们真正需要的是一个 COM vtable ):

function Intf2Obj(x: IInterface): TObject;
type
TStub = array[0..3] of Byte;
const
// ADD [ESP+$04], imm8; [ESP+$04] in stdcall is Self argument, after return address
add_esp_04_imm8: TStub = ($83, $44, $24, $04);
// ADD [ESP+$04], imm32
add_esp_04_imm32: TStub = ($81, $44, $24, $04);

function Match(L, R: PByte): Boolean;
var
i: Integer;
begin
for i := 0 to SizeOf(TStub) - 1 do
if L[i] <> R[i] then
Exit(False);
Result := True;
end;

var
p: PByte;
begin
p := PPointer(x)^; // get to vtable
p := PPointer(p)^; // load QueryInterface stub address from vtable

if Match(p, @add_esp_04_imm8) then
begin
Inc(p, SizeOf(TStub));
Result := TObject(PByte(Pointer(x)) + PShortint(p)^);
end
else if Match(p, @add_esp_04_imm32) then
begin
Inc(p, SizeOf(TStub));
Result := TObject(PByte(Pointer(x)) + PLongint(p)^);
end
else
raise Exception.Create('Not a Delphi interface implementation?');
end;

type
TAction = reference to procedure;

procedure Go;
var
a: TAction;
i: IInterface;
o: TObject;
begin
a := procedure
begin
Writeln('Hey.');
end;
i := PUnknown(@a)^;
o := i as TObject; // Requires Delphi 2010
o := Intf2Obj(i); // Workaround for non-D2010
Writeln(o.ClassName);
end;

begin
Go;
end.

这将(当前)打印Go$0$ActRec;但是如果你有第二个匿名方法,结构上相同,它将产生第二个方法,因为匿名方法体不会比较结构相等性(这将是一个高成本、低值(value)的优化,因为程序员不太可能会这样做)做这样的事情,大型结构比较并不便宜)。

如果您使用的是更高版本的 Delphi,您可以在该对象的类上使用 RTTI 并尝试比较字段,并自行实现结构比较。

关于delphi - 如何比较包含对象函数/过程的 TFunc/TProc?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5153486/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com