+program BetterRecordComparer;
+ Generics.Defaults, Rtti, TypInfo, Hash;
+function GetEqualsOperator(const typeInfo: PTypeInfo): Pointer;
+ EqualsOperatorName = '&op_Equality';
+ parameters: TArray<TRttiParameter>;
+ for method in ctx.GetType(typeInfo).GetMethods(EqualsOperatorName) do
+ if method.MethodKind <> mkOperatorOverload then
+ if method.CallingConvention <> ccReg then
+ parameters := method.GetParameters;
+ if (Length(parameters) = 2)
+ and (parameters[0].ParamType.Handle = typeInfo) and (parameters[1].ParamType.Handle = typeInfo)
+ and (pfConst in parameters[0].Flags) and (pfConst in parameters[1].Flags) then
+ Exit(method.CodeAddress);
+function GetGetHashCode(const typeInfo: PTypeInfo): Pointer;
+ for method in ctx.GetType(typeInfo).GetMethods('GetHashCode') do
+ if method.MethodKind <> mkFunction then
+ if method.CallingConvention <> ccReg then
+ if method.ReturnType.Handle <> System.TypeInfo(Integer) then
+ if method.GetParameters = nil then
+ Exit(method.CodeAddress);
+ class operator Implicit(const value: string): TMyRec;
+ class operator Equal(const left, right: TMyRec): Boolean;
+ function GetHashCode: Integer;
+ TEqualsOperator = function(const left, right): Boolean;
+ TGetHashCode = function(self: Pointer): Integer;
+ PComparerInstance = ^TComparerInstance;
+ TComparerInstance = record
+ Equals: TEqualsOperator;
+ GetHashCode: TGetHashCode;
+function NopQueryInterface(inst: Pointer; const IID: TGUID; out Obj): HResult; stdcall;
+ Result := E_NOINTERFACE;
+function MemAddref(inst: PComparerInstance): Integer; stdcall;
+ Result := AtomicIncrement(inst^.RefCount);
+function MemRelease(inst: PComparerInstance): Integer; stdcall;
+ Result := AtomicDecrement(inst^.RefCount);
+function Equals_Method(inst: PComparerInstance; const left, right): Boolean;
+ Result := inst^.Equals(left, right);
+function GetHashCode_Method(inst: PComparerInstance; value: Pointer): Integer;
+ if inst.size <= 4 then // check for 64bit
+ Result := inst^.GetHashCode(@value)
+ Result := inst^.GetHashCode(value);
+ EqualityComparer_Vtable_Method: array[0..4] of Pointer =
+function MakeInstance(vtable: Pointer; size: Integer;
+ equals: TEqualsOperator; getHashCode: TGetHashCode): Pointer;
+ inst: PComparerInstance;
+ GetMem(inst, SizeOf(inst^));
+ inst^.Vtable := vtable;
+ inst^.Equals := equals;
+ inst^.GetHashCode := getHashCode;
+function _LookupVtableInfo(intf: TDefaultGenericInterface; info: PTypeInfo; size: Integer): Pointer;
+ equalsMethod, getHashCodeMethod: Pointer;
+ if (intf = giEqualityComparer) and (info.Kind = tkRecord) then
+ equalsMethod := GetEqualsOperator(info);
+ getHashCodeMethod := GetGetHashCode(info);
+ if Assigned(equalsMethod) and Assigned(getHashCodeMethod) then
+ Result := MakeInstance(@EqualityComparer_Vtable_Method, size, equalsMethod, getHashCodeMethod);
+ if not Assigned(Result) then
+ Result := Generics.Defaults._LookupVtableInfo(intf, info, size);
+class operator TMyRec.Equal(const left, right: TMyRec): Boolean;
+ Result := left.value = right.value;
+function TMyRec.GetHashCode: Integer;
+ Result := THashBobJenkins.GetHashValue(Value[Low(string)], Length(value) * SizeOf(Char), 0);
+class operator TMyRec.Implicit(const value: string): TMyRec;
+ c: IEqualityComparer<TMyRec>;
+ c := IEqualityComparer<TMyRec>(_LookupVtableInfo(giEqualityComparer, TypeInfo(TMyRec), SizeOf(TMyRec)));
+ Writeln(c.Equals(r1, r2));
+ Writeln(r1.GetHashCode);
+ Writeln(c.GetHashCode(r1));
+ Writeln(c.GetHashCode(r2));