我想我对编写代码的方式的理解仍然非常有限。我尝试从SEARCH GENERIC LISTS修改解决方案,但我不能以他接受任意关键字作为搜索参数的方式更改代码
unit Unit_TsearchableTList;
interface
uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.Buttons,
contnrs, Generics.Collections;
type
TSearchableObjectList<T: class> = class(TObjectList<T>)
public type
TPredicate = reference to function(aItem: T; asearchValue: String): boolean;
public
function search(aFound: TPredicate<T>; asearchValue: String): T;
end;
implementation
function TSearchableObjectList<T>.search(aFound: TPredicate<T>;
asearchValue: String): T;
var
item: T;
begin
for item in Self do
* * * * * * * * COMPILE ERROR IS HERE * * * * * * * * * * * * * * * *
* ! ! ! ! ! !
if aFound(item, asearchValue) then
Exit(item);
Result := nil;
end;
end.
用法示例:
type
TReplaceElementNames = class
FindName: String;
ReplaceName: String;
ReplacementCondition: TReplacementCondition; // not relevant code
end;
var
LookUpList: TList<TReplaceElementNames>;
search : TReplaceElementNames;
begin
LookUpList := TSearchableObjectList<TReplaceElementNames>.Create;
search := LookUpList.search(
function(aItem: TReplaceElementNames; searchname: String): boolean
begin
Result := aItem.FindName = searchname;
end);
答案 0 :(得分:2)
您在代码中定义的类型为TPredicate
。但是你继续使用TPredicate<T>
,这是SysUtils
中定义的类型。只需更换
TPredicate<T>
带
TPredicate
在您的代码中,它将编译。
话虽如此,如果您在接受的答案中使用了代码,那会更简单。因为变量捕获用于提供搜索字符串,所以不需要两个参数谓词。
答案 1 :(得分:1)
根据定义,谓词是somethign,只需要一个参数,如http://docwiki.embarcadero.com/Libraries/XE2/en/System.SysUtils.TPredicate
那么,我们如何才能将更多参数传递给它,比如要比较什么?我们通过捕获ocntext并生成固定上下文的新临时函数来实现。
修改你的例子(我试着详细,以便你可以跟踪这些步骤):
Type CanCompareWithString<T> = function (const Value:T; const Target: string): boolean;
// overcoming Delphi limitation
Function CreatePredicateFunction<T>(Const Target: String; Const Matcher: CanCompareWithString<T>): TPredicate<T>;
begin
Result := function // creating new function (more correct: new closure)
(Arg1: T): Boolean // see top link to Delphi docs
begin
Result :=
Matcher // capturing the passed function
( Arg1,
Target ) // capturing the target value
end; // finished creating closure
end;
TSearchableObjectList<T: class> = class(TObjectList<T>)
public
function search(aFound: TPredicate<T>): T; overload;
function search( asearchValue: String; Matcher: CanCompareWithString<T>): T; overload;
end;
function TSearchableObjectList<T>.search(aFound: TPredicate<T>): T;
var
item: T;
begin
for item in Self do
if aFound(item) then
Exit(item);
Result := Default(T);
end;
function TSearchableObjectList<T>.search( asearchValue: String; Matcher: CanCompareWithString<T>): T;
begin
Result :=
Search(
CreatePredicateFunction(
Matcher, aSearchValue)
);
end;
你怎么用?希望是这样的:
function StringMatcher(const Value:string; const Target: string): boolean;
begin
Result := Value = Target;
end;
var L_S : TSearchableObjectList<String>; S: String;
S := L_S.Search('abcde', StringMAtcher);
现在你可能会问“为什么?为什么要使用StringMatcher?”
问题在于,在Delphi中,您无法在具有类型的运算符上添加部分约束(Scala中的内容称为外部类型AFAIR)。你就是无法编译
function Equal<T>(const value: String): boolean;
begin
Result := T = Value;
end;
function Sum<U>(const value1, value2: U): U;
begin
Result := value1 + value2;
end;
Delphi现在不是那个类型T,也不能确定它是否可以与字符串进行比较,因此它无法编译“T = Value”。
Delphi现在不是U的类型,也不能确定它是否可以添加,因此它无法编译“value1 + value2”。
这是一个很大的限制 - 但事实就是如此。
查看以下类型的实现:
你会看到需要哪种样板来完成一个类似的任务(但更简单一个!那里的任务只是比较T到T,而不是T到String)。