如何将通用过程TProc <t1,t2>作为参数传递并调用它?

时间:2016-09-28 11:42:52

标签: delphi generics delphi-xe5

我有一个日志类,它链接到许多模块。这个类的主要方法是类方法:

type
  TSeverity = (seInfo, seWarning, seError);

TLogger = class
  class procedure Log(AMessage: String; ASeverity: TSeverity);
end;

在其他地方,我有一个函数DoSomething(),它会执行一些我想记录的事情。但是,我不想将记录器的所有模块链接到&#39; DoSomething()&#39;声明使用记录器。相反,我想将任意日志记录方法作为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;

2 个答案:

答案 0 :(得分:1)

在这里引入泛型并没有帮助。您拥有的实际参数不是通用的。它们具有固定类型stringInteger。您传递给它的函数不是通用的,并且接收类型为stringTSeverity的参数。这些类型不匹配。

泛型无法帮助您,因为您的类型都是提前知道的。这里没有通用的东西。不知何故,您需要做的是在IntegerTSeverity之间进行转换。一旦你能做到这一点,你就可以打电话给你的功能。

在您的情况下,您应该通过一个接受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; ),因为它们更加灵活,以后可能会派上用场。

相关问题