【问题标题】:How to use a helper generic of type class of type如何使用类型类的辅助泛型
【发布时间】:2015-01-27 11:39:35
【问题描述】:

我正在尝试弄清楚如何泛化这个辅助方法;以便它返回与请求相同的类型:

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

使用约束指定泛型

约束项包括:

  • 零、一种或多种接口类型
  • 零或一类类型
  • 保留字“constructor”、“class”或“record”

(强调我的)

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

所以现在我被困住了。为了节省自己输入 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.

【问题讨论】:

  • 伊恩@XE6!? D5怎么了? ;)
  • 只看了3-4页我怎么知道谁写了这个问题:)
  • @NGLN 与时俱进。我们继续前进!
  • @LURD 确实如此。我是从手机的第一页获得的!
  • @NGLN 我的雇主决定为新版本支付 3,000 美元。三个月后,这个错误的升级已经过时了:(

标签: delphi generics delphi-xe6


【解决方案1】:

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

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.

【讨论】:

  • 你确定这是正确的语法吗?我在T: 中间得到E2029 ',', ';' or '&gt;' expected but ':' found。另外,如果T 被限制为TForm 类型,那么我不需要TForm 类型吗?
  • 在我的 XE7 中编译。我在您的约束部分中获取代码并将 TFormClass 更改为 TForm。并删除实现中的约束。无需重复。
  • 我已更新问题以包含一个完整的非功能性控制台测试应用程序。你能把你的代码给我看看,我们可以比较一下为什么你的不能编译?
  • 是的,你说在帮助器中不可能有泛型。所以我消除了无关的混乱。如果您愿意,我可以从帮助程序类TScreenHelper 调用TScreenHelperCore。我也可以删除问题,然后再问一次,除了删除对 "helper" 的引用可以严格关注 "generics" 问题。
  • 原来我错了。我知道什么?!你需要找一个真正的专家。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-21
  • 2017-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多