当使用内联泛型类函数比较两个托管记录和const参数时,会在记录上调用CopyRecord。 这会导致记录中的指针发生变化,并且两个(先前)相等的记录不会测试相等。
然而..... 在创建一个简单的MVCE时,我无法重现该行为。
以下代码可以正常使用:
program RTLTestManagedRecords;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
TCompare<T> = class
class function Compare(const Left, Right: T): integer; static; inline;
end;
TTest<T> = class
private
class var F: TCompare<T>;
public
class function Fast(const Left, Right: T): integer; static;
class procedure Test(const Left, Right: T; const message: string = ''); static;
end;
function BinaryCompare(const Left, Right; Size: NativeInt): integer; forward;
class function TCompare<T>.Compare(const Left, Right: T): integer;
begin
if GetTypeKind(T) = tkRecord then case SizeOf(T) of
0: Result:= 0;
1: Result:= (PByte(@Left)^)- (PByte(@Right)^);
2: Result:= (PWord(@Left)^)- (PWord(@Right)^);
4: Result:= (integer(PCardinal(@Left)^> PCardinal(@Right)^)-
integer(PCardinal(@Left)^< PCardinal(@Right)^));
else Result:= BinaryCompare(Left, Right, SizeOf(T));
end;
end;
{pointermath on}
function BinaryCompare(const Left, Right; Size: NativeInt): integer;
var
i: integer;
L,R: PByte;
begin
L:=@Left;
R:=@Right;
for i:= 0 to Size - 1 do begin
if L[i] <> R[i] then exit(L[i] - R[i]);
end;
Result:= 0;
end;
{$pointermath off}
type
TManagedRec = record
a: integer;
b: string;
end;
var
L,R: TManagedRec;
{ TTest<T> }
class function TTest<T>.Fast(const Left, Right: T): integer;
begin
Result:= F.Compare(Left, Right);
end;
class procedure TTest<T>.Test(const Left, Right: T; const message: string);
begin
try
WriteLn(Format(message,[TTest<T>.Fast(Left,Right)]));
except
WriteLn('Oops');
end;
end;
begin
L.a:= 1;
R.a:= 2;
L.b:= '7878787';
R.b:= '7777777';
TTest<TManagedRec>.Test(L,L,'Compare(L,L) = %d');
WriteLn(Format('Compare(L,R) = %d',[TCompare<TManagedRec>.Compare(L,R)]));
WriteLn(Format('Compare(R,L) = %d',[TCompare<TManagedRec>.Compare(R,L)]));
WriteLn(Format('Compare(R,R) = %d',[TCompare<TManagedRec>.Compare(R,R)]));
WriteLn(Format('Compare(L,L) = %d',[TCompare<TManagedRec>.Compare(L,L)]));
ReadLn;
end.
当我在更复杂的fastdefaults代码中运行它时,它会失败,因为CopyRecord被调用,即使参数被声明为const。 将它们声明为const [ref]并不能解决问题。 删除内联修复了它,但这消除了比较功能的许多好处。
问题
是否有人能够改变/扩展上面的MVCE,以便它就像更大的例子那样轰炸?&#34;
我试图了解问题的实际触发器是什么。
请不要评论比较托管记录的愚蠢行为
我知道Delphi在运行时不会在相同的字符串上进行字符串折叠
请不要建议解决问题的方法。我已经知道了解决方法。
TCompare<T> = class
class function Compare(const Left, Right: T): integer; static;
请注意,TCompare<T>
类是通用的,因此它应该能够比较任何内容。包括托管记录。编译器应始终遵守const
和。{
TCompare<T>.Compare(ManagedRec1, ManagedRec1)
应始终返回0
我理解这一点
TCompare<T>.Compare(AManagedRec, SameManagedRec)
并不总是返回0
我很好。
我不熟悉的是Delphi在const参数上调用_CopyRecord
。