我想创建具有某些功能(包括搜索功能)的通用记录。为了执行此搜索,我想传递一个自定义的比较器来比较项目。如果它是一个类,我可以在构造函数中传递它,但是我想使用一条记录来避免创建或释放它。因此,我没有在哪里初始化比较器的构造函数。
然后,我决定像类型参数一样传递Comparer类,并在需要比较器时,使用TComparer.Default创建比较器的实例。
好吧,这是代码:
TMyArray<T,C: TComparer<T>> = record
FComparer: IComparer<T>;
Items: TArray<T>;
function Contains<AItem: T>: Boolean;
end;
Run Code Online (Sandbox Code Playgroud)
当我尝试以这种方式使用它时,出现问题:
TMyRecord = record
Score: Real;
A: string;
B: string;
end;
TMyRecordComparer = class(TComparer<TMyRecord>)
function Compare(Left, Right: TMyRecord): Integer;
end;
TMyRecordArray = TMyArray<TMyRecord, TMyRecordComparer>;
Run Code Online (Sandbox Code Playgroud)
有了最后一个声明,我得到以下错误:E2515类型参数'T'与类型'System.Generics.Defaults.TComparer \'不兼容。
任何想法如何解决这个问题?
虽然David的答案解决了编译错误的问题,但实际上我会使用其他约束:
TMyArray<T; C: class, constructor, IComparer<T>> = record
Run Code Online (Sandbox Code Playgroud)
这意味着C必须是具有无参数构造函数的类并实现IComparer<T>接口。因为您不必从继承继承比较器,System.Generics.Collections.TComparer<T>而只实现所需的IComparer<T>接口,所以可以稍微放松约束。
同样,首先,在问题中发布代码后,您还将收到W1010警告,这意味着您缺少TMyRecordComparer.Compare方法的重写(及其方法const)。如果您使用虚拟抽象方法TComparer<T>实现继承,IComparer<T>则需要这样做。
您使用的想法.Default也将不起作用,因为它会为type的比较器创建默认实现T。但是您想使用您指定的自定义名称。
因此,根据我在上面编写的约束,您可以执行类似以下操作(天真的非线程安全实现):
function TMyArray<T, C>.Contains<AItem>: Boolean;
begin
if FComparer = nil then
FComparer := C.Create;
// ....
end;
Run Code Online (Sandbox Code Playgroud)
最后但并非最不重要的一点是,我不确定您的Contains方法是否正确-编写方法的方式建议您检查数组是否包含AItem类型T为子类型或子类型的元素(或者是否T为接口类型,实现T)-我猜你打算写
function Contains(AItem: T): Boolean;
Run Code Online (Sandbox Code Playgroud)
问题是约束的语法:
TMyArray<T,C: TComparer<T>>
Run Code Online (Sandbox Code Playgroud)
这既约束T和C要TComparer<T>。
相反,您需要这样做:
TMyArray<T; C: TComparer<T>>
Run Code Online (Sandbox Code Playgroud)
该文档提供了各种语法选项的示例。