【问题标题】:How to cast a variable of specific type to generic T in Delphi?如何在 Delphi 中将特定类型的变量转换为泛型 T?
【发布时间】:2018-05-11 12:19:51
【问题描述】:

根据T的实际类型,我想返回一个具体的值;这是一个简化的示例(围绕某个对象的记录包装器):

function TMyRec.TryGet<T>(const Default: T): T;
begin
  if TypeInfo(T) = TypeInfo(TMyObject) then
    Result:= FMyObject {E2010 Incompatible types}
  else Result:= FMyObject.TryGet<T>(Default);
end;

很明显,我不能将除T 之外的任何东西分配给Result,并且as TT() 不起作用,当T 不限于class 时...

也不可能进行仅在通用限制上有所不同的重载:

function TryGet<T: TMyObject>(const Default: T): T; overload;
{...}
Result:= FMyObject as T; {here it would work}

为了帮助理解下面 cmets 中的讨论,以下是我编辑之前的示例:

function TMyRec.TryGet<T>(const Default: T): T;
begin
  if TypeInfo(T) = TypeInfo(TMyObject) then Result:= FMyObject
  //else ...
  else Result:= Default;
end;

【问题讨论】:

  • 如果 T 标记为 TryGet&lt;T: class&gt; 类,那么您可以在 Delphi XE 和更新版本中执行 MyObject as T。然后,您还可以在 Delphi 2010 及更高版本中执行 T(MyObject)
  • 最简单的方法是您在答案中使用的方法,恕我直言。
  • 我只是refer to Eric Lippert here,但您可能会认为在这种情况下通用解决方案可能是错误的解决方案。
  • @maf-soft 我无法给您写答案,因为我不知道您实际上要解决什么问题。我只能说,鉴于您尝试解决它的方式,泛型可能不是正确的解决方案。
  • 这对我来说似乎没有多大意义。如果您知道类型是什么,为什么要使用泛型。我很困惑,我想你也是。

标签: delphi generics casting type-conversion delphi-10.2-tokyo


【解决方案1】:

我相信这是实现您想要的最佳方法(至少在性能方面)。

type 
  PMyObject = ^TMyObject;

function TMyRec.TryGet<T>(const Default: T): T;
begin
  if TypeInfo(T) = TypeInfo(TMyObject) then 
    PMyObject(@Result)^ := FMyObject
  else if TypeInfo(T) = TypeInfo(Integer) then 
    PInteger(@Result)^ := 1
  //else ...
  else
    Result := Default;
end;

【讨论】:

  • 太好了,所以 TValue 不是唯一的方法,很高兴知道另一种方法。为什么你认为这是最好的方法?对我来说,它看起来像一个黑客。但是,我不明白为什么有人已经对此表示反对,它增加了价值并且很有帮助。谢谢。反对者应始终张贴他们的理由。
  • @maf 在 meta 上已经讨论过很多次了,投票是匿名的。你不需要证明你的投票方式是正确的。上或下。这是一个非常糟糕的答案。有两张赞成票。我很想知道它是如何被赞成的。
  • @David:这有什么不好?当我离开我的 Delphi 设置时,我或多或少也考虑过发布:不要将结果值转换为 T,将 Result 转换为类型(间接)。这不是您通常应该使用泛型的方式,但有时可能会发生这种情况。 IMO 比 RTTI 更可取。是的,当然这是一个 hack。一般来说,一个人不应该那样投。通常,您要么不使用泛型,要么不强制转换。
  • 当然,有时您不希望在uses 子句中使用Rtti,或者您不喜欢(小)开销。所以很高兴知道我可以这样做。但对我来说,Rtti 看起来不那么骇人听闻,而且更具可读性,所以这次我决定更喜欢它。
  • 如果类型是从 TMyObject 派生的呢?或者是不同的积分类型?这是一个糟糕的方法,错误地实现了。
【解决方案2】:

我发现的唯一方法是使用System.Rtti.TValue(错误类型除外):

Result:= TValue.From(FMyObject).AsType<T>

或更短 ;-) 具有自动类型(如果类型错误,则返回 false;您可以在这里忽略它,因为您已经检查了 TypeInfo):

TValue.From(FMyObject).TryAsType(Result)

建议:重新思考你的概念;有concerns 表示泛型可能不是一个好的解决方案,但如果不了解更多背景信息,我们就无法判断。

【讨论】:

  • 请不要在答案中提问。
  • 我认为这个解决方案非常好,我会使用它,但如果我不喜欢使用TValue 怎么办 - 还有其他聪明的方法吗?期待更多答案...
  • @DavidHeffernan,@J,我只是想鼓励其他人发布其他答案,如果我不知道还有其他或更有趣的解决方案。这不是一个真正的问题。真正的问题在于问题,一个好的答案在于答案,以帮助有同样问题的人。你所做的就是吹毛求疵。不是每个问号都是严重的罪行。反正我删了...
【解决方案3】:

我正在使用 Delphi XE5。

将 yoru 示例更改为:

function TryGet<T: class>(const Default: T): T; overload;
{...}
Result:= FMyObject as T; {here it will work}

更改为 TMyObjectclass。当你不是“class”它时,你会得到编译器错误 E2089。

【讨论】:

  • 但要求是它必须适用于任何类型。这也已经在 cmets 中讨论过了。
  • @maf-soft 你是对的。每个人都在寻找答案。我也是。我不读cmets。我也错过了 any 类型的要点,因为第一句话是“(围绕某个对象的记录包装器)”。请注意:请仔细阅读。
  • 对我来说,只要删除这个答案就可以了 :)
猜你喜欢
  • 1970-01-01
  • 2015-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多