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

Ian*_*oyd 4 delphi generics delphi-xe6

我试图弄清楚如何对这个辅助方法进行泛化 ; 以便它返回与要求相同的类型:

type
    TScreenHelper = class helper for TScreen
    public
        function FindForm(DesiredFormClass: TFormClass): TForm;
    end;
Run Code Online (Sandbox Code Playgroud)

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

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

   frmReportReminderSetup := TfrmReportReminderSetup(Screen.FindForm(TfrmReportReminderSetup));
Run Code Online (Sandbox Code Playgroud)

非通用实现是:

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;
Run Code Online (Sandbox Code Playgroud)

泛型

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

frmContoso := Screen.FindForm(TfrmContoso);
Run Code Online (Sandbox Code Playgroud)

在伪代码中,我想要的签名是这样的:

function FindForm(T: TFormClass): T;
Run Code Online (Sandbox Code Playgroud)

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

function FindForm(<T>): T;
Run Code Online (Sandbox Code Playgroud)

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

function FindForm<T>: T;
Run Code Online (Sandbox Code Playgroud)

试试吧

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;
Run Code Online (Sandbox Code Playgroud)

除了编译失败:

Result := nil;  E2010 Incompatible types: 'T' and 'Pointer'
Run Code Online (Sandbox Code Playgroud)

我可以看看有什么不对劲.它不明白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;
Run Code Online (Sandbox Code Playgroud)

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

frmContoso := Screen.FindForm<TfrmConsoto>();
Run Code Online (Sandbox Code Playgroud)

但是不要紧; 它是泛型的方式.

除非这是无效的

语法:

function FindForm<T: TFormClass>: T;
Run Code Online (Sandbox Code Playgroud)

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

泛型中的约束

使用约束指定泛型

约束项包括:

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

(强调我的)

虽然我允许一种类型,但我没有传递类型; 我正在传递一类课程类型.

所以现在我被卡住了.在我试图保存自己输入25个字符时,我现在已经在德尔福仿制药的细节上花了一个小时.

TL;博士

如何泛化:

function FindForm(DesiredFormClass: TFormClass): TForm;
Run Code Online (Sandbox Code Playgroud)

示例非工作代码

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.
Run Code Online (Sandbox Code Playgroud)

Dav*_*nan 5

你的约束是错误的.代替

function FindForm<T: TFormClass>: T;
Run Code Online (Sandbox Code Playgroud)

你需要

function FindForm<T: TForm>: T;
Run Code Online (Sandbox Code Playgroud)

您将使用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.
Run Code Online (Sandbox Code Playgroud)