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