We use a TList<TFunc<Boolean>>
with some function ... of object
s in it and now want to Remove()
some of the entries again. But it doesn't work because obviously you simply can not compare these reference to ...
thingies reliably.
Here's some test code:
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.
We tried virtually everything, = operator, comparing pointers, etc..
We even tried some really nasty things like repeatedly casting to PPointer
and dereferencing until we get equal values, but that of course didn't yield satisfying results either =).
- Case (2), (4) and (5) are OK, as there are in fact distinct functions.
- Case (3) is trivial and OK, too.
- Case (1) is what we want to detect, and this is what we can't get to work.
I fear, Delphi stealthily creates two distinct anonymous functions that forward the call to Foo.Bar
. In this case we'd be completely powerless, unless we wanted to wade through a morass of unknown memory... and well, we don't.
var F: TFunc<Boolean>; ShowMessage(IntToStr(SizeOf(F)));
- it shows 1 for my Delphi 2010! how can that be? – TerrieterrierIsEqual
are 4 bytes apart (could be alignment, though). – ShetlerF
as function call or function / method pointer / reference is built into Pascal; you can blame Wirth or Hejlsberg, the first for making no-arg invocation implicit (no () operator needed like C or C++), the second for not requiring disambiguation when introducing general procedure pointers into the language (Wirth Pascal only permitted downward funargs owing to the problem of passing nested procedures, which ironically is the same problem that method references fix, by using reference counting to keep data from the outer frame alive). – Clubby