【问题标题】:How do I use a generic method in a derived type如何在派生类型中使用泛型方法
【发布时间】:2013-10-22 10:41:17
【问题描述】:

这看起来相当简单,也许我只是缺少一点语法胶水......这是我的简单通用(Delphi XE3)示例:

unit Unit1;

interface

uses
  generics.collections;

type

X = class
public
  Id: Integer;
end;

XList<T : X> = class( TObjectList<T> )
 function Find(Id: Integer) : T;
end;

Y = class(X)

end;

YList = class(XList<Y>)
end;

implementation

{ XList<T> }

function XList<T>.Find(Id: Integer): T;
var
  t: X;
begin
  for t in Self do
    if t.Id = Id then
      Result := t;
end;

end.

这不会与“[dcc32 Error] Unit1.pas(41): E2010 Incompatible types: 'Y' and 'X'”一起编译。归根结底:

YList = class(XList<Y>)
end;

Y 派生自 X 那么为什么会有问题呢?

【问题讨论】:

    标签: delphi generics inheritance delphi-xe3


    【解决方案1】:

    Alex 的答案是解决问题的正确方法。并且一旦知道答案就从函数中返回也很好。

    我想通过更多解释来扩展答案。我特别想回答您在 cmets 中对 Alex 的回答提出的问题:

    顺便说一句......为什么原来的作品不起作用? T 派生自 X。

    问题代码在这里:

    function XList<T>.Find(Id: Integer): T;
    var
      t: X;
    begin
      for t in Self do
        if t.Id = Id then
          Result := t;
    end;
    

    考虑泛型的方法是想象当您实例化类型并提供具体类型参数时代码的样子。在这种情况下,让我们将T 替换为Y。那么代码是这样的:

    function XList_Y.Find(Id: Integer): Y;
    var
      t: X;
    begin
      for t in Self do
        if t.Id = Id then
          Result := t;
    end;
    

    现在你在分配给Result的那一行遇到了问题:

    Result := t;
    

    好吧,ResultY 类型,但 tX 类型。 XY 之间的关系是Y 派生自X。所以Y 的实例是X。但是X 的实例不是Y。所以这个赋值是无效的。

    正如 Alex 正确指出的,您需要将循环变量声明为 T 类型。我个人会这样写代码:

    function XList<T>.Find(Id: Integer): T;
    begin
      for Result in Self do
        if Result.Id = Id then
          exit;
      Result := nil;
      // or perhaps you wish to raise an exception if the item cannot be found
    end;
    

    这也解决了您的搜索例程在未找到项目的情况下未初始化其返回值的问题。这是一个问题,一旦您获得代码,就实际编译而言,您的编译器会警告您。我希望您启用编译器警告,并在它们出现时处理它们!

    【讨论】:

      【解决方案2】:

      我不得不重新实现 Find 方法来修复它:

      { XList<T> }
      
      function XList<T>.Find(Id: Integer): T;
      var
        item: T;
      begin
        for item in Self do
          if item.Id = Id then
            Exit(item);
        Result := nil;
      end;
      

      这里重要的是将变量声明中使用的类型从X 替换为T

      然后我只是将变量从t 重命名为item 以避免与类型占位符T 发生名称冲突,并将Result := item 替换为Exit(item) 以返回找到的项目并退出该方法。

      【讨论】:

      • 好东西@AlexSC。我最初有'var t:T'。我忘记了不区分大小写。我的 C# 泛型东西妨碍了那里!
      • 顺便说一句......为什么原来的作品不起作用? T 派生自 X。
      • @Rob:对不起,我没有答案。对我来说,编译器在单元的最后一行找到了错误消息,这表明它与泛型类型实例化有关。看来编译器对泛型的类型匹配非常严格。
      • @Rob,如果您想知道原因,请阅读 David 的回答:stackoverflow.com/a/9142020/800214
      • @whosrdaddy 实际上,它比一般差异更平淡无奇。我添加了一个答案来解释。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      • 2012-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-22
      • 2014-06-19
      相关资源
      最近更新 更多