Delphi 如何通过通用程序TProc<;T1、T2>;作为参数并调用它?
我有一个日志类,它链接到许多模块。此类的主要方法是类方法:Delphi 如何通过通用程序TProc<;T1、T2>;作为参数并调用它?,delphi,generics,delphi-xe5,Delphi,Generics,Delphi Xe5,我有一个日志类,它链接到许多模块。此类的主要方法是类方法: type TSeverity = (seInfo, seWarning, seError); TLogger = class class procedure Log(AMessage: String; ASeverity: TSeverity); end; 在其他地方,我有一个函数DoSomething(),它做一些我想记录的事情。但是,我不想将记录器的所有模块链接到其中声明“DoSomething()”使用记录器的模块。相
type
TSeverity = (seInfo, seWarning, seError);
TLogger = class
class procedure Log(AMessage: String; ASeverity: TSeverity);
end;
在其他地方,我有一个函数DoSomething()
,它做一些我想记录的事情。但是,我不想将记录器的所有模块链接到其中声明“DoSomething()”使用记录器的模块。相反,我希望将任意日志方法作为DoSomething的参数传递,并从其主体调用它
问题是TLogger.Log
需要在logger类中定义的tVerity
类型的参数。所以我无法定义类型:
type
TLogProcedure = procedure(AMessage: String; ASverity: TSeverity) of Object;
因为我必须包含一个单元,其中声明了tVerity
我试图根据一般程序提出一些解决方案,但我被卡住了
uses
System.SysUtils;
type
TTest = class
public
class function DoSomething<T1, T2>(const ALogProcedure: TProc<T1,T2>): Boolean; overload;
end;
implementation
class function TTest.DoSomething<T1, T2>(const ALogProcedure: TProc<T1, T2>): Boolean;
var
LMessage: String;
LSeverity: Integer;
begin
//Pseudocode here I would like to invoke logging procedure here.
ALogProcedure(T1(LMessage), T2(LSeverity));
end;
谢谢你的帮助
更新
也许我没说清楚
unit uDoer;
interface
type
TLogProcedure = procedure(AMessage: String; AErrorLevel: Integer) of Object;
// TDoer knows nothing about logging mechanisms that are used but it allows to pass ALogProcedure as a parameter.
// I thoight that I can somehow generalize this procedure using generics.
type
TDoer = class
public
class function DoSomething(const ALogProcedure: TLogProcedure): Boolean;
end;
implementation
class function TDoer.DoSomething(const ALogProcedure: TLogProcedure): Boolean;
begin
ALogProcedure('test', 1);
Result := True;
end;
end.
使用其中一个记录机制的单独单元
unit uLogger;
interface
type
TSeverity = (seInfo, seWarning, seError);
// I know that I could solve my problem by introducing an overloaded method but I don't want to
// do it like this. I thought I can use generics somehow.
TLogger = class
class procedure Log(AMessage: String; ASeverity: TSeverity); {overload;}
{class procedure Log(AMessage: String; ASeverity: Integer); overload;}
end;
implementation
class procedure TLogger.Log(AMessage: String; ASeverity: TSeverity);
begin
//...logging here
end;
{class procedure TLogger.Log(AMessage: String; ASeverity: Integer);
begin
Log(AMessage, TSeverity(ASeverity));
end;}
end.
两个单元的示例用法
implementation
uses
uDoer, uLogger;
procedure TForm10.FormCreate(Sender: TObject);
begin
TDoer.DoSomething(TLogger.Log); //Incompatible types: Integer and TSeverity
end;
在这里引入泛型没有帮助。您拥有的实际参数不是通用参数。它们有固定类型、
string
和Integer
。将它们传递给的函数不是泛型函数,它接收类型为string
和TSeverity
的参数。这些类型不匹配
泛型在这里帮不了你,因为你的类型都是提前知道的。这里没有通用的东西。不知何故,您需要做的是在Integer
和TSeverity
之间进行转换。一旦你能做到这一点,你就可以调用你的函数了
在您的情况下,您应该传递一个接受整数的过程,因为在调用该过程的点上没有可用的t验证。然后在该过程的实现中,调用接受TSeverity
的函数,这就是转换的地方
在涉及通用过程类型的场景中,您遇到的情况非常常见。您有一个通用的过程类型,如下所示:
type
TMyGenericProcedure<T> = procedure(const Arg: T);
unit uDoer;
interface
type
TLogProcedure = reference to procedure(const AMessage: String; AErrorLevel: Integer);
// TDoer knows nothing about logging mechanisms that are used but it allows to pass ALogProcedure as a parameter.
type
TDoer = class
public
class function DoSomething(const ALogProcedure: TLogProcedure): Boolean;
end;
implementation
class function TDoer.DoSomething(const ALogProcedure: TLogProcedure): Boolean;
begin
ALogProcedure('test', 1);
Result := True;
end;
end.
类型
TMyGenericProcedure=程序(常数Arg:T);
为了调用这样一个过程,您需要T
的一个实例。如果从T
上的泛型函数调用过程,则参数也必须是泛型的。在您的情况下,该参数不是泛型的,它被固定为Integer
。在这一点上,您尝试使用泛型的尝试就不复存在了
说了这么多,你所描述的根本就不是一回事。如果您不知道此时的TSeverity
是什么,您怎么可能提出严重性参数?那对我来说毫无意义。你怎么能变出一个整数值,并希望它匹配这个枚举类型?一些温和的重新设计将使您能够在不进行任何类型转换的情况下非常简单地执行此操作。正如David Heffernan所说,您不能以这种方式使用泛型。相反,您应该使用一个函数将错误级别映射到严重性类型,并使用它将两者粘合在一起。根据您更新的示例,可以对其进行如下修改:
type
TMyGenericProcedure<T> = procedure(const Arg: T);
unit uDoer;
interface
type
TLogProcedure = reference to procedure(const AMessage: String; AErrorLevel: Integer);
// TDoer knows nothing about logging mechanisms that are used but it allows to pass ALogProcedure as a parameter.
type
TDoer = class
public
class function DoSomething(const ALogProcedure: TLogProcedure): Boolean;
end;
implementation
class function TDoer.DoSomething(const ALogProcedure: TLogProcedure): Boolean;
begin
ALogProcedure('test', 1);
Result := True;
end;
end.
然后,您可以提供将错误级别转换为严重性的粘合过程:
implementation
uses
uDoer, uLogger;
function SeverityFromErrorLevel(const AErrorLevel: Integer): TSeverity;
begin
if (AErrorLevel <= 0) then
result := seInfo
else if (AErrorLevel = 1) then
result := seWarning
else
result := seError;
end;
procedure LogProc(const AMessage: String; AErrorLevel: Integer);
var
severity: TSeverity;
begin
severity := SeverityFromErrorLevel(AErrorLevel);
TLogger.Log(AMessage, severity);
end;
procedure TForm10.FormCreate(Sender: TObject);
begin
TDoer.DoSomething(LogProc);
end;
实现
使用
乌多尔,乌洛格;
函数SeverityFromErrorLevel(常量错误级别:整数):t验证;
开始
如果(AerorLevel)您不需要泛型,因为您知道参数的类型。第一个是字符串,第二个是整数。但是TSeverity和integer不兼容,因此过程签名将不同,Delphi将不允许我将过程(a:string;B:TSeverity)作为DoSomething()的参数传递它需要一个签名过程:过程(a:String;B:Integer)是您的参数和整数还是TSeverity?我不想添加此依赖项。好的。然后从日志模块中提取TSeverity
的定义,将其放在单独的模块中,并在两个位置使用该新模块。它现在不再是日志模块的一部分,因此您没有此依赖项。@Wodzu当然,您可以决定不管你想要什么,我只是告诉你,你的决定不会把你带到任何地方。请看看我最新的答案,也许现在它会更清楚。这个问题以前是非常清楚的,也许你可以重新阅读我的答案,看看它是否对你更清楚。如果你执意使用泛型,那么就这样做吧。我已经重读了好几遍了mes和我知道将整数转换为TSeverity是不安全的。我只是想在这里使用泛型作为练习,但我被卡住了,这就是为什么我要问这个问题。泛型不会让你摆脱这种不安全感。要么你让TSeverity
可用,要么你转换。泛型如何帮助这两种情况呢?但再一次,你似乎不知道我认为泛型可能不是解决问题的办法。当我第一次这么说的时候,你告诉我不要提出这样的建议,如果你想用泛型来解决这个问题,那么你会这么做。坦白地说,我认为你需要打开你的头脑。我想我可以把整数转换成T2,这将是一个严重的问题。