【问题标题】:How can I convert from generic to Variant in Delphi如何在 Delphi 中从泛型转换为变体
【发布时间】:2012-02-14 01:38:45
【问题描述】:

我有一个 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...

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

【问题讨论】:

  • 您是否尝试过创建Variant 类型的局部变量,将SomeValue 分配给它,然后将局部变量传递给FMyIntf.DoStuff()
  • 是的。我不能这样做,因为没有从 'T' 到 'Variant' 的有效转换...

标签: delphi generics delphi-xe rtti variant


【解决方案1】:

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

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

【讨论】:

  • 我担心它会是这样的:-P 在TValue的内部经过一番探讨之后,结果Boolean的TypeKind是tkEnumeration,并且TValue在调用时会引发异常AsVariant 在具有 TypeKind tkEnumeration 的值上。这也意味着您的示例-尽管它没有引发异常-仍然返回错误的变体,因为我的布尔值被转换为'AsOrdinal',导致整数类型的变体-不是布尔值...也许我需要同时检查typekind 和 typename....
  • @MathiasFalkenberg 我已经编辑了我的答案以正确处理布尔值。
  • +1。您可以通过将尽可能多的类型转换为可以作为变体的某种表示来制作最佳的 GenericToVariant。如果需要,您可以添加代码来转换重要的本地类 (TObject) 类型,使用新的 TObject.ToString 方法。
  • 似乎只有布尔值是个问题。我想知道将@Linas 示例中的case 语句缩短到仅考虑tkEnumeration 和其他所有内容是否还不够。 TValue.AsVariant 为不受支持的类型抛出它自己的异常,并且 tkInteger、tkInt64、tkFloat、tkString 等似乎使用 TValue.AsVariant 可以很好地转换......
  • 其实这样不行吗? if val.TypeInfo = TypeInfo(Boolean) then Result := val.AsBoolean else Result := val.AsVariant;
【解决方案2】:

另一种方式(测试XE10)

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

【讨论】:

    【解决方案3】:

    看起来在我的 Delphi 10.2 版中,布尔问题已经消失,TValue.From&lt;T&gt;(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;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多