2012-01-19 1 views
11

У меня есть общий класс Delphi, который предоставляет функцию с аргументом родового типа. Внутри этой функции мне нужно передать экземпляр типового типа на другой объект, ожидающий тип Variant. Аналогично этому:Как преобразовать из generic в Variant в Delphi

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 ...

Есть ли лучший способ сделать это преобразование? Мне нужно только работать для простых встроенных типов (за исключением перечислений и записей)

+0

Вы пытались создать локальную переменную типа 'Variant', присвойте ему' SomeValue', а затем передайте локальную переменную в 'FMyIntf.DoStuff()'? –

+0

Да. Я не могу этого сделать, потому что нет никакого действительного отбрасывания от «Т» до «Варианта» ... –

ответ

10

Я думаю, что нет прямого способа конвертировать общий тип в вариант, потому что вариант не может содержать все возможные типы. Вы должны написать свою конкретную процедуру преобразования. Например .:

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 
+0

Я боялся, что это будет что-то вроде этого :-P После того, как немного ворвался во внутреннюю часть TValue, оказывается, что TypeKind of Boolean является tkEnumeration, а TValue вызывает исключение при вызове AsVariant при значении, имеющем TypeKind tkEnumeration. Это также означает, что ваш пример - хотя он и не вызывает исключения - все еще возвращает неправильный вариант, так как мой Boolean преобразуется в «AsOrdinal», в результате возникает вариант интегрального типа - не логический ... Возможно, мне нужно проверить как typekind и typename .... –

+0

@MathiasFalkenberg Я отредактировал свой ответ, чтобы правильно обрабатывать булевы. – Linas

+0

+1. Вы можете создать оптимальный GenericToVariant, преобразовывая столько типов, сколько хотите преобразовать, в некоторое представление, которое может быть вариантом. Если вы хотите, вы можете добавить код для преобразования важных типов локального класса (TObject), используя новый метод TObject.ToString. –

0

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

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