2012-01-19 8 views
11

Tengo una clase genérica Delphi que expone una función con un argumento del tipo genérico. Dentro de esta función, necesito pasar una instancia del tipo genérico a otro objeto esperando un tipo de Variante. Similar a esto:¿Cómo puedo convertir de genérico a variante en 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; 

He intentado utilizar Rtti.TValue.From (SomeValue) .AsVariant. Esto funcionó para tipos integrales, pero explotó para booleanos. No entiendo muy bien por qué, ya que normalmente podría asignar un valor booleano a una variante ...

¿Hay alguna forma mejor de realizar esta conversión? Solo necesito que funcione para tipos simples incorporados (excluyendo enumeraciones y registros)

+0

Ha intentado crear una variable local de tipo 'Variant', asigne' SomeValue' al mismo, y luego pase la variable local a 'FMyIntf.DoStuff()'? –

+0

Sí. No puedo hacer eso porque no hay un elenco válido de 'T' a 'Variant' ... –

Respuesta

10

Creo que no hay una forma directa de convertir el tipo genérico a variante porque la variante no puede contener todos los tipos posibles. Debe escribir su rutina de conversión específica. Ej .:

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; 

Debido TValue.AsVariant maneja la mayor parte de las conversiones de tipos internamente, esta función se puede simplificar. Voy a manejar las enumeraciones en caso de que usted pueda necesitar más adelante:

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; 

Posible uso:

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

Temía que fuera algo como eso :-P Después de hurgar un poco en las partes internas de TValue, Resulta que TypeKind of Boolean es tkEnumeration, y TValue genera una excepción cuando llama a AsVariant en un valor que tiene TypeKind tkEnumeration. Lo cual también significa que su ejemplo -aunque no genera una excepción- aún devuelve la Variante incorrecta, ya que mi Boolean se convierte en 'AsOrdinal', lo que da como resultado una Variante de tipo integral - no booleana ... Tal vez deba verificar tanto el typekind y typename .... –

+0

@MathiasFalkenberg He editado mi respuesta para manejar booleanos correctamente. – Linas

+0

+1. Puede crear un GenericToVariant óptimo convirtiendo tantos tipos como desee convertir en una representación que pueda ser una variante. Si lo desea, podría agregar código para convertir tipos de clases locales importantes (TObject), utilizando el nuevo método TObject.ToString. –

0

Otra forma (probado XE10)

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