如何在 Delphi 中从泛型转换为变体

发布于 12-27 17:49 字数 575 浏览 5 评论 0原文

我有一个 Delphi 泛型类,它公开一个带有泛型类型参数的函数。在这个函数中,我需要将泛型类型的实例传递给另一个需要 Variant 类型的对象。与此类似:

type
  IMyInterface = interface
    DoStuff(Value: Variant);
  end;      

  TMyClass<T> = class
    FMyIntf: IMyInterface
    procedure DoStuff(SomeValue: T);
  end;

[...]

procedure MyClass<T>.DoStuff(SomeValue: T);
begin
  FMyIntf.DoStuff((*convert SomeValue to Variant here*));
end;

我尝试使用 Rtti.TValue.From(SomeValue).AsVariant。这对于整数类型有效,但对于布尔类型则不然。我不太明白为什么,因为通常我可以为 Variant 分配一个布尔值......

有没有更好的方法来进行这种转换?我只需要它适用于简单的内置类型(不包括枚举和记录)

I have a Delphi generic class that exposes a function with an argument of the generic type. Inside this function, I need to pass an instance of the generic type on to another object expecting a Variant type. Similar to this:

type
  IMyInterface = interface
    DoStuff(Value: Variant);
  end;      

  TMyClass<T> = class
    FMyIntf: IMyInterface
    procedure DoStuff(SomeValue: T);
  end;

[...]

procedure MyClass<T>.DoStuff(SomeValue: T);
begin
  FMyIntf.DoStuff((*convert SomeValue to Variant here*));
end;

I tried using Rtti.TValue.From(SomeValue).AsVariant. This worked for integral types, but blew up for Booleans. I don't quite see why, since normally I'd be able to assign a Boolean value to a Variant...

Is there a better way to make this conversion? I only need it to work for simple built-in types (excluding enumerations and records)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

甜尕妞2025-01-03 17:49:24

我认为没有直接的方法将泛型类型转换为变体,因为变体无法容纳所有可能的类型。您必须编写特定的转换例程。例如:

interface
//...
type
  TDemo = class
  public
    class function GetAsVariant<T>(const AValue: T): Variant;
  end;
//...
implementation
uses
  Rtti,
  TypInfo;
//...

{ TDemo}

class function TDemo.GetAsVariant<T>(const AValue: T): Variant;
var
  val: TValue;
  bRes: Boolean;
begin
  val := TValue.From<T>(AValue);
  case val.Kind of
    tkInteger: Result := val.AsInteger;
    tkInt64: Result := val.AsInt64;
    tkEnumeration: 
    begin
      if val.TryAsType<Boolean>(bRes) then
        Result := bRes
      else
        Result := val.AsOrdinal;
    end;
    tkFloat: Result := val.AsExtended;
    tkString, tkChar, tkWChar, tkLString, tkWString, tkUString:
      Result := val.AsString;
    tkVariant: Result := val.AsVariant
    else
    begin
      raise Exception.Create('Unsupported type');
    end;
  end;
end;

由于TValue.AsVariant在内部处理大部分类型转换,因此可以简化该函数。我将处理枚举,以防您稍后需要它们:

class function TDemo.GetAsVariant<T>(const AValue: T): Variant;
var
  val: TValue;
begin
  val := TValue.From<T>(AValue);
  case val.Kind of
    tkEnumeration:
    begin
      if val.TypeInfo = TypeInfo(Boolean) then
        Result := val.AsBoolean
      else
        Result := val.AsOrdinal;
    end
    else
    begin
      Result := val.AsVariant;
    end;
  end;

可能的用法:

var
  vValue: Variant;
begin
  vValue := TDemo.GetAsVariant<Boolean>(True);
  Assert(vValue = True); //now vValue is a correct Boolean

I think there is no direct way to convert generic type to variant because variant cannot hold all the possible types. You must write your specific conversion routine. E.g.:

interface
//...
type
  TDemo = class
  public
    class function GetAsVariant<T>(const AValue: T): Variant;
  end;
//...
implementation
uses
  Rtti,
  TypInfo;
//...

{ TDemo}

class function TDemo.GetAsVariant<T>(const AValue: T): Variant;
var
  val: TValue;
  bRes: Boolean;
begin
  val := TValue.From<T>(AValue);
  case val.Kind of
    tkInteger: Result := val.AsInteger;
    tkInt64: Result := val.AsInt64;
    tkEnumeration: 
    begin
      if val.TryAsType<Boolean>(bRes) then
        Result := bRes
      else
        Result := val.AsOrdinal;
    end;
    tkFloat: Result := val.AsExtended;
    tkString, tkChar, tkWChar, tkLString, tkWString, tkUString:
      Result := val.AsString;
    tkVariant: Result := val.AsVariant
    else
    begin
      raise Exception.Create('Unsupported type');
    end;
  end;
end;

Because TValue.AsVariant handles most of the type conversions internally, this function can be simplified. I will handle enumerations in case you could need them later:

class function TDemo.GetAsVariant<T>(const AValue: T): Variant;
var
  val: TValue;
begin
  val := TValue.From<T>(AValue);
  case val.Kind of
    tkEnumeration:
    begin
      if val.TypeInfo = TypeInfo(Boolean) then
        Result := val.AsBoolean
      else
        Result := val.AsOrdinal;
    end
    else
    begin
      Result := val.AsVariant;
    end;
  end;

Possible usage:

var
  vValue: Variant;
begin
  vValue := TDemo.GetAsVariant<Boolean>(True);
  Assert(vValue = True); //now vValue is a correct Boolean
绾颜2025-01-03 17:49:24

看起来在我的 Delphi 版本 10.2 中,布尔问题已经消失,并且 TValue.From(FValue).AsVariant 就足够了。

这是一个包含其他一些有用内容的示例,例如比较泛型类型:

  TMyValue<T> = class(TPersistent)
  private
    FValue: T;
    procedure SetValue(const AValue: T);
    function GetAsVariant: Variant; override;
  public
    procedure Assign(Source: TPersistent); override;
    property Value: T read FValue write SetValue;
    property AsVariant: Variant read GetAsVariant;
  end;

function TMyValue<T>.GetAsVariant: Variant;
begin
  Result:= TValue.From<T>(FValue).AsVariant;
end;

procedure TMyValue<T>.SetValue(const AValue: T);
begin
  if TEqualityComparer<T>.Default.Equals(AValue, FValue) then Exit;
  FValue:= AValue;
  //do something
end;

procedure TMyValue<T>.Assign(Source: TPersistent);
begin
  if Source is TMyValue<T> then Value:= (Source as TMyValue<T>).Value
  else inherited;
end;

Looks like in my Delphi version 10.2 the Boolean problem is gone and TValue.From<T>(FValue).AsVariant is enough.

Here an example with some other helpful things like comparing the generic type:

  TMyValue<T> = class(TPersistent)
  private
    FValue: T;
    procedure SetValue(const AValue: T);
    function GetAsVariant: Variant; override;
  public
    procedure Assign(Source: TPersistent); override;
    property Value: T read FValue write SetValue;
    property AsVariant: Variant read GetAsVariant;
  end;

function TMyValue<T>.GetAsVariant: Variant;
begin
  Result:= TValue.From<T>(FValue).AsVariant;
end;

procedure TMyValue<T>.SetValue(const AValue: T);
begin
  if TEqualityComparer<T>.Default.Equals(AValue, FValue) then Exit;
  FValue:= AValue;
  //do something
end;

procedure TMyValue<T>.Assign(Source: TPersistent);
begin
  if Source is TMyValue<T> then Value:= (Source as TMyValue<T>).Value
  else inherited;
end;
梦途2025-01-03 17:49:24

另一种方式(测试过XE10)

Var
  old : variant;
  val : TValue;
Begin
  val := TValue.FromVariant(old);
End;

Another way (tested XE10)

Var
  old : variant;
  val : TValue;
Begin
  val := TValue.FromVariant(old);
End;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文