I want to create a generic record with some functionality, including a search function. To perform this search I wanted to pass a custom Comparer to compare items. If it were a class, I could pass it in the constructor, but I want to use a record to avoid to create or free it. So, I don't have a constructor where to initialize the Comparer.
Then I decided to pass the Comparer class like a Type parameter, and when I need the comparer, create a instance of the comparer using TComparer.Default.
Well, here is the code:
TMyArray<T,C: TComparer<T>> = record
FComparer: IComparer<T>;
Items: TArray<T>;
function Contains<AItem: T>: Boolean;
end;
The problem appears when I try to use it in this way:
TMyRecord = record
Score: Real;
A: string;
B: string;
end;
TMyRecordComparer = class(TComparer<TMyRecord>)
function Compare(Left, Right: TMyRecord): Integer;
end;
TMyRecordArray = TMyArray<TMyRecord, TMyRecordComparer>;
With this last declaration, I get this error: E2515 Type parameter 'T' is not compatible with type 'System.Generics.Defaults.TComparer\'.
Any idea how to solve this issue?
While David's answer fixes the problem with the compile error, I would actually use a different constraint:
TMyArray<T; C: class, constructor, IComparer<T>> = record
That means C
must be a class with a parameterless constructor and implement the IComparer<T>
interface. That relaxes the constraint a little, as you don't have to inherit your comparer from System.Generics.Collections.TComparer<T>
but only implement the needed IComparer<T>
interface.
Also, fwiw, with the code posted in your question you will get a W1010 warning which means you are missing an override on your TMyRecordComparer.Compare
method (and its missing const
). This is needed if you inherit from TComparer<T>
as that one implements IComparer<T>
with a virtual abstract method.
Your idea to use .Default
will also not work as that one creates a default implementation for a comparer for type T
. But you want to use the custom one you specified.
So with the constraint I wrote above you then can do something like this (naive non-threadsafe implementation):
function TMyArray<T, C>.Contains<AItem>: Boolean;
begin
if FComparer = nil then
FComparer := C.Create;
// ....
end;
And last but not least, I am not sure your Contains
method is correct - the way you wrote it suggests that you want to check if your array contains an element of type AItem
that is of type T
or a sub type (or if T
is an interface type, implements T
) - I would guess you meant to write
function Contains(AItem: T): Boolean;
The problem is the syntax of your constraint:
TMyArray<T,C: TComparer<T>>
This constrains both T
and C
to be TComparer<T>
.
Instead you need this:
TMyArray<T; C: TComparer<T>>
The documentation gives examples of the various syntax options.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With