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,这将是一个严重的问题。