【问题标题】:Delphi: generics and 'is'-operator problemDelphi:泛型和“is”运算符问题
【发布时间】:2009-11-19 16:47:56
【问题描述】:

基于较早的post,我编写了以下代码。请原谅这篇文章的冗长。我认为最好让所有各方都有完整的代码可供测试和评论。

program sandbox;
{$APPTYPE CONSOLE}

uses
  SysUtils,
  Generics.Collections;

type
  TDataType = class
    // Stuff common to TInt and TStr
  end;

  TInt = class(TDataType)
    FValue:  integer;
    constructor Create(Value, Low, High: integer);
  end;

  TStr = class(TDataType)
    FValue: string;
    constructor Create(Value: string; Length: integer);
  end;

  TSomeClass = class
    FIntList: TList<TInt>;
    FStrList: TList<TStr>;
    procedure AddToList<T: TDataType>(Element: T);
    constructor Create();
    procedure Free();
  end;

constructor TInt.Create(Value, Low, High: Integer);
begin
  inherited Create();
  FValue := Value;   
end;

constructor TStr.Create(Value: string; Length: Integer);
begin
  inherited Create();
  FValue := Value;
end;

procedure TSomeClass.AddToList<T>(Element: T);
begin
  if TObject(Element) is TInt then
    FIntList.Add(Element)
  else if TObject(Element) is TStr then
    FStrList.Add(Element);
end;

constructor TSomeClass.Create();
begin
  inherited;
  FIntList := TList<TInt>.Create();
  FStrList := TList<TStr>.Create();
end;

procedure TSomeClass.Free();
var
  SomeIntItem: TInt;
  SomeStrItem: TStr;
begin
  for SomeIntItem in FIntList do begin
    SomeIntItem.Free();
  end;

  for SomeStrItem in FStrList do begin
    SomeStrItem.Free;
  end;

  FIntList.Free();
  FStrList.Free();
end;

var
  Inst: TSomeClass;

begin
  try
    { TODO -oUser -cConsole Main : Insert code here }

    Inst := TSomeClass.Create;
    Inst.AddToList(TInt.Create(100, 0, 101));
    Inst.AddToList(TStr.Create('Test', 10));
    Inst.Free;

  except
    on E:Exception do
    Writeln(E.Classname, ': ', E.Message);
  end;
end.

请注意,现实世界中TIntTStr 的构造函数也将使用Low, High: integerLength: integer 参数。我在运行 Delphi 2009 时在 if TObject(Element) is TInt thenelse if TObject(Element) is TStr then 遇到“E2089 Invalid typecast”。有人知道为什么会这样吗?

编辑:请注意TIntTStr 只是可能的 10-20 种其他类型中的两种;否则重载是工作的工具。 :)

【问题讨论】:

  • 您的 AddToList 声明和实现不同。我对泛型知之甚少,但对于常规帕斯卡,那是错误的。
  • 嗨列文!我认为您指的是实现“procedure TSomeClass.AddToList(Element: T)”,而不是签名“procedure AddToList(Element: T)”?如果是这样,对您的评论的回应是 RAD 工作室不希望对实际实施进行限制。
  • 啊,我明白了。对于那些还没有进入泛型的人来说,至少最明显的现在已经不在了。感谢您清理它。
  • 只是出于好奇:将每种数据类型存储在单独的列表中有什么意义?为什么不使用TList &lt;TDataType&gt;?您真的必须分别处理每种数据类型吗?你不能提取一个共同的签名并使用多态性吗?
  • 感谢粉碎机!老实说,我什至没有想到这一点。但是,我不确定它在我的真实代码中的方式。 TInt 的所有实例在逻辑上与 TStr 的实例是分开的。所以把它们分开是相当理智的......但很明显,我有很多关于多态性的知识要学习。在这一点上,我将退后一步,考虑其他设计选项。顺便说一句:我仍然不明白为什么 typecast 是非法的。

标签: delphi generics casting


【解决方案1】:

重新考虑您的设计。您可以只使用重载而不是泛​​型类型参数,如下所示:

procedure Add (SomeString : TString); overload;
procedure Add (SomeInt : TInt); overload;

或者,如果您想使用多态性,请按照 Gamecat 的建议进行操作,只需将基本类型作为参数传递,在该参数上使用 is

procedure Add (Element : TDataType);        

就像 Rob 在对您之前的问题的评论中指出的那样:如果您只允许两种类型并且具有基于实际类型的条件,那么它并不是真正的通用。所以泛型在这里可能是错误的工具。

希望对您有所帮助。

【讨论】:

  • 它没有编译器错误。它缺少类型转换;-)。但我同意错误的工具评论。
  • 感谢粉碎机!我意识到我应该从一开始就指出 TInt 和 TStr 还将伴随着 10 多种其他类型。我不知道 TypeInfo() 方法,所以 +1! :)
  • 你是说我刚刚删除的那个? :) 如果有人想知道,这是 Barry Kelly 原始答案的链接:stackoverflow.com/questions/805931/…
  • 是的,就是这个!我意识到这可能是常识,但我大约四个月前开始使用 Delphi,所以从所有标准来看,我都是新手。也感谢您的设计意见。
  • @conciliator:即使您有 10 多种不同的类型,如果您想将它们放在 10 多种不同的列表中,泛型不是可行的方法,重载方法更好(对此答案+1 )。无论如何,如果您对 Delphi 比较陌生,我强烈建议您远离泛型,除非它们有真正令人信服的案例 - 至少在 Delphi 2009 中,我们的开发人员已被视为对这一新事物的巨大现场测试功能,你永远不知道是你搞砸了还是编译器在事情不工作时应该受到责备。
【解决方案2】:

问题不在于泛型。您将 TDataType 添加到需要 TInt 或 TStr 的列表中:

procedure TSomeClass.AddToList<T>(Element: T);
begin
  if TObject(Element) is TInt then
    FIntList.Add(TInt(Element))
  else if TObject(Element) is TStr then
    FStrList.Add(TStr(Element));
end;

解决问题。

但为什么不使用:

procedure TSomeClass.AddToList(Element: TDataType);
begin
  if Element is TInt then
    FIntList.Add(TInt(Element))
  else if Element is TStr then
    FStrList.Add(TStr(Element));
end;

【讨论】:

  • 谢谢,Gamecat!最后一个例子编译得很好。但是,使用您的第一个示例(尝试 T:TDataType,T:类和空约束)时,我仍然遇到类型转换错误。你编译了吗?
  • @Gamecat:也许我一开始并不清楚?是语句“如果 TObject(Element) 是 TInt then”和“else if TObject(Element) is TStr then”中的类型转换失败了……你知道为什么吗?
  • 我刚刚测试了你的代码。 TObject(Element)cast 工作正常,而 FIntList.Add (Element) 产生“无效类型:T 和 TInt”错误消息 - 正如 Gamecat 所说。
  • 这很奇怪。我上传了一个屏幕转储:img12.imageshack.us/img12/1208/typecasterror.png。我做错了什么?
  • 你使用的是哪个 Delphi 版本?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-11
  • 1970-01-01
  • 2010-12-19
  • 2010-10-16
  • 1970-01-01
  • 1970-01-01
  • 2016-06-10
相关资源
最近更新 更多