【问题标题】:Converting a generic type variable to string将泛型类型变量转换为字符串
【发布时间】:2021-10-25 11:15:44
【问题描述】:

我正在尝试将T 类型的通用变量转换为string

  TMyTest = class
    class function GetAsString<T>(const AValue : T) : string; static;
  end;

...

uses
  System.Rtti;

class function TMyTest.GetAsString<T>(const AValue : T) : string;
begin
  Result := TValue.From<T>(AValue).ToString();
end;

使用多种类型(如IntegerDoubleBoolean...)效果很好,但使用Variant 变量会“失败”。

procedure TForm1.FormCreate(Sender: TObject);
var
  Tmp : Variant;
begin
  Tmp := 123;

  ShowMessage(TMyTest.GetAsString<Variant>(Tmp));
end;

它产生以下输出:

(变体)

我期待VarToStr 函数获得相同的输出(但我不能将该函数与泛型变量一起使用):

123

【问题讨论】:

标签: delphi generics type-conversion delphi-xe7


【解决方案1】:

您可以检查T 是否是变体,然后在AsVariant 函数上使用VarToStr

您可以轻松地扩展该功能以适应 ToString 不会给您预期结果的其他类型。

uses
  System.TypInfo, System.Rtti;

class function TMyTest.GetAsString<T>(const AValue : T) : string;
begin
  if PTypeInfo(TypeInfo(T)).Kind = tkVariant then
    Result := VarToStr(TValue.From<T>(AValue).AsVariant)
  else
    Result := TValue.From<T>(AValue).ToString();
end;

【讨论】:

  • If TypeInfo(T) = TypeInfo(Variant) then 会更好。或者,在 XE7+ 中,您可以改用 GetTypeKind()。这两种方法都允许编译器在编译时丢弃未使用的分支,因此您应该能够使用VarToStr(AValue)而不涉及TValue。检查PTypeInfo.Kind 将不允许这样做,因为它需要运行时检查,因此必须编译两个分支。
  • @RemyLebeau 您能否将其添加为单独的答案。我保持我的代码与 XE4 兼容,所以我一直忘记存在其他更好的选择。
【解决方案2】:

您可以check the type of T using RTTI,然后在TVariant 时调用VarToStr(),例如:

class function TMyTest.GetAsString<T>(const AValue : T) : string;
begin
  if TypeInfo(T) = TypeInfo(Variant) then begin
    // yes, the following cast looks odd, but the compiler can't validate
    // T is really a Variant in this context, as it syntax-checks the code 
    // *before* instantiating the Generic, so a runtime cast is needed.
    // The TypeInfo check above will ensure the cast is safe...
    Result := VarToStr({AValue}PVariant(@AValue)^);
  end else begin
    Result := TValue.From<T>(AValue).ToString;
  end;
end;

或者,在 XE7+ 中,您可以改用 GetTypeKind() 内在函数:

class function TMyTest.GetAsString<T>(const AValue : T) : string;
begin
  if GetTypeKind(T) = tkVariant then begin
    Result := VarToStr({AValue}PVariant(@AValue)^);
  end else begin
    Result := TValue.From<T>(AValue).ToString;
  end;
end;

这两种方法都允许编译器通过从可执行文件中删除未使用的分支来优化代码,因为两种比较都被视为编译时常量。


注意:TValue.ToString 不支持的其他TTypeKind 值类型是tkUnknowntkArraytkRecordtkDynArray

【讨论】:

  • Result := TValue.FromVariant(AValue).AsString;Result := VarToStr(AValue); 在任何版本中都不起作用,因为E2010 Incompatible types: 'Variant' and 'T' 所以你仍然需要使用TValue 的类型转换或转换。
  • Result := VarToStr(AValue); 应该在 XE7+ 版本中工作。在 XE7 之前的版本中,即使 TypeInfo(T) = TypeInfo(Variant) 可以在编译时验证,我猜编译器仍然不知道 TVariant,因此需要进行类型转换。
  • 这是我的想法,但编译器不同意。我尝试了 10.3.3 和 10.4.2,当然还有 XE4。我没有中间版本来测试它是否在某个时候有效。
  • 我现在没有安装 any 版本来测试,但我可以发誓这在某个时候有效。也许我错了。哦,好吧...
  • @DalijaPrasnikar "如果它在某个时候起作用会很好" - apparently not.
猜你喜欢
  • 2019-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-29
  • 2011-10-31
  • 2010-09-05
  • 1970-01-01
  • 2015-01-29
相关资源
最近更新 更多