将一个通用类型变量转换为字符串

4

我正在尝试将类型为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;

使用几种类型(如 Integer, Double, Boolean ...) 可以很好地工作,但是使用 Variant 变量时会“失败”。

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

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

它会产生以下输出:

(variant)

我期望得到与 VarToStr 函数相同的输出(但是我无法将该函数用于通用变量):

123


1
相关:在Delphi中测试泛型类型 - Remy Lebeau
2个回答

6

您可以检查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;

1
如果使用 TypeInfo(T) = TypeInfo(Variant) 会更好。或者在 XE7+ 中,您可以使用 GetTypeKind()。这两种方法都允许编译器在编译时丢弃未使用的分支,因此您应该能够使用 VarToStr(AValue) 而不涉及 TValue。检查 PTypeInfo.Kind 将不允许这样做,因为它需要运行时检查,所以必须编译两个分支。 - Remy Lebeau
1
@RemyLebeau,你能把它作为单独的答案添加吗?我保持我的代码与XE4兼容,所以我经常忘记其他更好的选择的存在。 - Dalija Prasnikar

3
你可以使用RTTI检查T的类型,当T是一个Variant时,可以调用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;

无论哪种方法,编译器都会将这两种比较视为编译时常量,并通过删除未使用的分支从可执行文件中优化代码。


注意:其他TTypeKind值类型不受TValue.ToString支持,包括tkUnknowntkArraytkRecordtkDynArray


无论是 Result := TValue.FromVariant(AValue).AsString; 还是 Result := VarToStr(AValue); 在任何版本中都不起作用,因为出现了 E2010 Incompatible types: 'Variant' and 'T' 错误。因此,您仍然需要使用类型转换或者通过 TValue 进行转换。 - Dalija Prasnikar
这就是我想的,但编译器不同意。我尝试了10.3.3和10.4.2,当然还有XE4。我没有中间版本来测试它是否在某个时候工作过。 - Dalija Prasnikar
我现在没有任何版本可供测试,但我曾经发誓过这个工作过。也许我错了。哦,算了... - Remy Lebeau
也许Fabrizio可以测试Result := VarToStr(AValue);在XE7中是否可行,如果它曾经可行过,那么知道这一点将是很好的,因为我们可以提交回归错误报告。这是我写的第一件事,但我不能说我是否期望它能够工作,因为在我使用的其他一些语言中类似的东西是可行的,或者我也看到它在Delphi中可行。 - Dalija Prasnikar
1
@DalijaPrasnikar:“知道它在某个时候是否有效会很好。” - 显然不行 - Remy Lebeau
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接