Как преобразовать из generic в Variant в Delphi

У меня есть общий класс 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. Это работало для интегральных типов, но взорвалось для булевых. Я не совсем понимаю, почему, поскольку обычно я мог бы присвоить значение Boolean для 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

Ответ 2

Другой способ (протестирован XE10)

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