我有一个日志类,它链接到许多模块。这个类的主要方法是类方法:
type
TSeverity = (seInfo, seWarning, seError);
TLogger = class
class procedure Log(AMessage: String; ASeverity: TSeverity);
end;
在其他地方,我有一个函数DoSomething()
,它会执行一些我想记录的事情。但是,我不想将记录器的所有模块链接到' DoSomething()'声明使用记录器。相反,我想将任意日志记录方法作为DoSomething的参数传递,并从其正文中调用它。
问题是TLogger.Log
需要TSeverity
类型的参数,该参数在logger类中定义。所以我无法定义类型:
type
TLogProcedure = procedure(AMessage: String; ASverity: TSeverity) of Object;
因为我必须包含一个声明TSeverity
的单位。
我试图提出一些基于通用程序的解决方案,但我被困住了。
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;
代码中的其他一些我想使用DoSomething
begin
TTest.DoSomething<String, TSeverity>(Log);
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;
答案 0 :(得分:1)
在这里引入泛型并没有帮助。您拥有的实际参数不是通用的。它们具有固定类型string
和Integer
。您传递给它的函数不是通用的,并且接收类型为string
和TSeverity
的参数。这些类型不匹配。
泛型无法帮助您,因为您的类型都是提前知道的。这里没有通用的东西。不知何故,您需要做的是在Integer
和TSeverity
之间进行转换。一旦你能做到这一点,你就可以打电话给你的功能。
在您的情况下,您应该通过一个接受Integer
的程序,因为您在调用该程序时没有TSeverity
可用。然后在该过程的实现中,您调用接受TSeverity
的函数,即转换的位置。
在涉及通用过程类型的场景中,您遇到的情况非常普遍。你有一个通用的程序类型:
type
TMyGenericProcedure<T> = procedure(const Arg: T);
要调用此类过程,您需要T
的实例。如果从T
上的通用函数调用过程,那么您的参数也必须是通用的。在您的情况下,该参数不是通用的,它固定为Integer
。此时,您尝试使用泛型解开。
说完这一切之后,你所描述的并没有完全挂在一起。如果你不知道那时TSeverity
是什么,你怎么能提出严重性论点呢?这对我没有任何意义。你怎么能想出一个整数值并希望它匹配这个枚举类型?一些温和的重新设计可以让您在没有任何类型转换的情况下完成此操作。
答案 1 :(得分:0)
正如David Heffernan所说,你不能以这种方式使用泛型。相反,您应该使用函数将错误级别映射到严重性类型,并使用它将两者粘合在一起。根据您更新的示例,可以像这样修改它:
$(())
然后,您可以提供将错误级别转换为严重性的粘合程序:
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;
),因为它们更加灵活,以后可能会派上用场。