如何使用类型类的类通用助手

时间:2014-11-28 19:38:05

标签: delphi generics delphi-xe6

我想弄清楚如何泛化这个帮助方法;以便它返回与要求相同的类型:

type
    TScreenHelper = class helper for TScreen
    public
        function FindForm(DesiredFormClass: TFormClass): TForm;
    end;

现在调用者必须将返回值强制转换为他们想要的类型:

var
   frmReportReminderSetup: TfrmReportReminderSetup;
begin
   //frmReportReminderSetup := Screen.FindForm(TfrmReportReminderSetup); Doesn't compile

   frmReportReminderSetup := TfrmReportReminderSetup(Screen.FindForm(TfrmReportReminderSetup));

非通用实现是:

function TScreenHelper.FindForm(DesiredFormClass: TFormClass): TForm;
var
    f: TForm;
    i: Integer;
begin
    Result := nil;

    for i := 0 to Screen.FormCount-1 do //Screen.Forms does not support enumeration
    begin
        f := Screen.Forms[i];

        if (f is DesiredFormClass) then
        begin
            Result := f;
            Exit;
        end;
    end;
end;

泛型

我想要的是使用泛型的一些方法,以便函数返回它所要求的类型。

frmContoso := Screen.FindForm(TfrmContoso);

在伪代码中,我想要的签名就像:

function FindForm(T: TFormClass): T;

当需要将其转换为实际的Delphi语法时,我认为你必须在尖括号中指定一个 T 引用:

function FindForm(<T>): T;

但我不认为那里允许 ;我认为它必须在左括号之前:

function FindForm<T>: T;

试试

TScreenHelper = class helper for TScreen
public
    function FindFormOld(DesiredFormClass: TFormClass): TForm;
    function FindForm<T>: T;
end;

function TScreenHelper.FindForm<T>: T;
var
    f: TForm;
    i: Integer;
begin
    Result := nil;

    for i := 0 to Screen.FormCount-1 do //Screen.Forms does not support enumeration
    begin
        f := Screen.Forms[i];

        if (f is T) then
        begin
            Result := f as T;
            Exit;
        end;
    end;
end;

除了编译失败:

Result := nil;  E2010 Incompatible types: 'T' and 'Pointer'

我可以看看有什么不对劲。它不明白 T 是一个类(即如果它是Integer怎么办?那么将它设置为nil绝对是错误的。)

约束

所以我需要以某种方式给编译器提供T 类型的提示:

TScreenHelper = class helper for TScreen
public
    function FindForm<T: TFormClass>: T;
end;

function TScreenHelper.FindForm<T: TFormClass>: T;
var
    f: TForm;
    i: Integer;
begin
    Result := nil;

    for i := 0 to Screen.FormCount-1 do //Screen.Forms does not support enumeration
    begin
        f := Screen.Forms[i];

        if (f is T) then
        begin
            Result := f as T;
            Exit;
        end;
    end;
end;

这个新签名令人困惑!你不再传递所需类型的函数。相反,您现在调用您想要的功能的变体:

frmContoso := Screen.FindForm<TfrmConsoto>();

但无论如何;它是泛型的方式。

除非无效

语法:

function FindForm<T: TFormClass>: T;

无效,因为TFormClass不是允许的Delphi约束类型之一:

  

Constraints in Generics

     

使用约束指定泛型

     

约束条款包括:

     
      
  • 零,一个或多个接口类型
  •   
  • 零或一个班级类型
  •   
  • 保留字“构造函数”,“类”或“记录”
  •   

(强调我的)

虽然我允许一个类型,但我没有传递类类型;我正在传递类型。

所以现在我被卡住了。在我试图保存自己输入25个字符的时候,我现在已经在Delphi泛型的细节上花了一个小时。

TL;博士

我如何泛化

function FindForm(DesiredFormClass: TFormClass): TForm;

示例非工作代码

program Project2;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, Vcl.Forms;

type
    TScreenHelperCore = class(TObject)
    public
        class function FindForm<T: TForm>: T;
    end;

    TfrmContoso = class(TForm)
    public

   end;

{ TScreenHelperCore }

class function TScreenHelperCore.FindForm<T: TForm>: T; 
//                                         \__[dcc32 Error] Project2.dpr(23): E2029 ',', ';' or '>' expected but ':' found
var
    f: TForm;
    i: Integer;
begin
    Result := nil;

    for i := 0 to Screen.FormCount-1 do //Screen.Forms does not support enumeration
    begin
        f := Screen.Forms[i];

        if (f is T) then
        begin
            Result := f;
            Exit;
        end;
    end;
end;

var
    f: TfrmContoso;

begin
  try
        f := TScreenHelperCore.FindForm<TfrmContoso>;
        if f = nil then
            f := TfrmContoso.Create(nil);

        f.ShowModal;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

1 个答案:

答案 0 :(得分:5)

你的约束是错误的。而不是

function FindForm<T: TFormClass>: T;

你需要

function FindForm<T: TForm>: T;

您将使用TMyForm而不是class of TMyForm来实例化此通用类型。

并且您必须仅在类的声明中声明约束,而不是在其实现中。这是一个完整的程序,编译:

{$APPTYPE CONSOLE}
uses
  Vcl.Forms;

type
  TScreenHelper = class helper for TScreen
  public
    function FindForm<T: TForm>: T;
  end;

function TScreenHelper.FindForm<T>: T;
var
  f: TForm;
  i: Integer;
begin
  for i := 0 to Screen.FormCount - 1 do
  begin
    f := Screen.Forms[i];
    if (f is T) then
    begin
      Result := T(f);
      Exit;
    end;
  end;
  Result := nil;
end;

type
  TMyForm = class(TForm)
  end;

var
  Form: TMyForm;

begin
  Form := Screen.FindForm<TMyForm>;
end.