admin管理员组文章数量:1400032
Using TEqualityComparer<T>.Default
for record types seems to "work" in some situations ("work" as in checking for value-equivalence across all fields between two records), but not in others. I'm sure that writing a custom comparer is a better alternative over using this one, but I'm curious as to what's causing the difference.
Specifically, it seems like if the record has a String
field, calling Equals()
on the two records themselves works, but accessing them inside a list does not, eg:
type
TestRec = record
Value: String;
end;
begin
var Rec1: TestRec;
var Rec2: TestRec;
Rec1.Value := 'a';
Rec2.Value := 'a';
var List1: TArray<TestRec> := [Rec1];
var List2: TArray<TestRec> := [Rec2];
var Comparer: IEqualityComparer<TestRec>;
Comparer := TEqualityComparer<TestRec>.Default;
Comparer.Equals(Rec1, Rec2); // returns true
Comparer.Equals(List1[0], List2[0]); // returns false
end;
This doesn't seem to happen when the record doesn't contain managed types like String
, so I'm guessing it's something related to memory since the default comparer uses CompareMem()
.
Using TEqualityComparer<T>.Default
for record types seems to "work" in some situations ("work" as in checking for value-equivalence across all fields between two records), but not in others. I'm sure that writing a custom comparer is a better alternative over using this one, but I'm curious as to what's causing the difference.
Specifically, it seems like if the record has a String
field, calling Equals()
on the two records themselves works, but accessing them inside a list does not, eg:
type
TestRec = record
Value: String;
end;
begin
var Rec1: TestRec;
var Rec2: TestRec;
Rec1.Value := 'a';
Rec2.Value := 'a';
var List1: TArray<TestRec> := [Rec1];
var List2: TArray<TestRec> := [Rec2];
var Comparer: IEqualityComparer<TestRec>;
Comparer := TEqualityComparer<TestRec>.Default;
Comparer.Equals(Rec1, Rec2); // returns true
Comparer.Equals(List1[0], List2[0]); // returns false
end;
This doesn't seem to happen when the record doesn't contain managed types like String
, so I'm guessing it's something related to memory since the default comparer uses CompareMem()
.
1 Answer
Reset to default 6Since your record type has a managed String
field, the Comparer
should not try to compare the raw memory of the two record instances, as it would be comparing the String
internal data pointers, not comparing the character data they point at.
But that is exactly what is happening in your example. In reality, TEqualityComparer
does raw memory comparisons, and so it only works with records that contain trivial non-managed fields, and have the same padding bytes in the record layout.
When you compare Rec1
and Rec2
directly, they compare as equal only because the two String
s happen to be pointing at the same memory block for the 'a'
literal. Change one of the String
s to a different value and they will not compare as equal anymore since their data pointers will be different. You can also force this in your example by calling UniqueString()
on the String
s, eg:
Rec1.Value := 'a';
Rec2.Value := 'a';
UniqueString(Rec1.Value);
UniqueString(Rec2.Value);
...
Comparer.Equals(Rec1, Rec2); // returns false!
When you compare the List
elements, the two String
s are pointing at separate memory blocks because the 'a'
data gets copied when each array is created, which is why the elements never compare as equal regardless of their values.
So, to compare a record type with managed fields, you MUST use a custom Comparer
that compares the record members one-by-one as appropriate for their types.
本文标签:
版权声明:本文标题:delphi - Why does the default equality comparer for records behave differently when the record contains certain fields and is ac 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744120469a2591714.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
TEqualityComparer<T>
is for collections like dictionaries that need a way to callGetHashCode
andEquals
for any type. If you have a concrete type, you don't need to use anIEqualityComparer<T>
to check for equality - just implement the Equals operator for your record type and use=
. – Stefan Glienke Commented Mar 27 at 8:34