2013-04-04 11 views
8

Qual è l'equivalente del tipo di dati VARIANT di C++ in C#?Tipo di dati VARIANT di C++ in C#

Ho codice in C++ che utilizza il tipo di dati VARIANT. Come posso convertire quel codice in C#?

+0

È necessario eseguire l'interoperabilità con il codice C++ o semplicemente convertire il codice? – Botz3000

+3

L'equivalente diretto è 'object' come tipo e' object.GetType() 'come" cosa c'è nella variante? " informazioni, ma ciò renderebbe un codice C# veramente scadente. Il contesto è importante. – Jon

+0

Se si utilizza C# 4.0, è possibile utilizzare il tipo di dati 'dinamico', ma come @Jon ha detto, ciò si tradurrà in un codice C# scadente, dal momento che si elude il tipo safty e il controllo in fase di compilazione. –

risposta

1

Questa è una domanda complicata.

Da C# 4, è possibile utilizzare dinamico per indicare che il tipo è noto in fase di esecuzione.

Per mia personale comprensione, tuttavia, C++ richiede il tipo noto in fase di compilazione. Pertanto potresti prendere in considerazione l'uso di object, ma object in C# è un tipo esistente.

Per il concetto di multi-tipo, valore singolo (polimorfismo AKA) di VARIANT, non è necessario trovare un tipo corrispondente in C#, basta definire le classi e le interfacce. Puoi sempre fare riferimento a un oggetto come all'interfaccia che la classe implementa.

Se si sta eseguendo il porting del codice e per capire una sintassi che è possibile utilizzare semplicemente in LHS e che la considerazione del tipo è nota al momento della compilazione, utilizzare var.

7

Bene, ci sono in realtà due varianti in C++: boost :: variant e variante COM. La soluzione segue più o meno la stessa idea, ma la prima è più complessa. Mi aspetto che tu intenda usare quest'ultimo.

Iniziamo col dire che è qualcosa che non dovresti usare se possibile. Detto questo, questo è come lo fai :-)

Varianti e interoperabilità

varianti sono a volte utilizzati in Interop di se avete bisogno la rappresentazione di byte per essere lo stesso.

Se hai a che fare con Interop, assicurati di controllare la classe VariantWrapper su MSDN e farlo funzionare così.

Varianti e porting considerazioni

varianti sono per lo più utilizzati in API, e di solito in questo modo:

void Foo(SomeEnum operation, Variant data); 

La ragione è fatto come questo in C++ è perché non v'è alcuna base object classe hai quindi bisogno di qualcosa di simile. Il modo più semplice per porto questo è quello di cambiare la firma:

void Foo(SomeEnum operation, object data); 

Tuttavia, se state portando in ogni caso, è anche seriamente prendere in considerazione questi due, dal momento che si risolvono al momento della compilazione e può salvarti il grande 'interruttore' che segue di solito in modo Foo:

void SomeOperation(int data); 
void SomeOperation(float data); 
// etc 

Varianti e byte di coerenza

in rari casi è necessario manipolare i byte stessi.

Essenzialmente la variante è solo una grande unione di tipi di valore racchiusi in un tipo di valore singolo (struct). In C++, è possibile allocare un tipo di valore sull'heap perché una struct è uguale a una classe (well sort-of). Il modo in cui viene utilizzato il tipo di valore è solo un po 'importante, ma ne parleremo più avanti.

Unione significa semplicemente che si stanno sovrapponendo tutti i dati in memoria. Si noti come ho notato esplicitamente il tipo di valore sopra; per le varianti questo è fondamentalmente di cosa si tratta. Questo ci dà anche un modo per testarlo, cioè controllando un altro valore nella struttura.

Il modo per fare questo in C# è quello di utilizzare l'attributo StructLayout in un tipo di valore, che funziona sostanzialmente come segue:

[StructLayout(LayoutKind.Explicit)] 
public struct Variant 
{ 
    [FieldOffset(0)] 
    public int Integer; 
    [FieldOffset(0)] 
    public float Float; 
    [FieldOffset(0)] 
    public double Double; 
    [FieldOffset(0)] 
    public byte Byte; 
    // etc 
} 

// Check if it works - shouldn't print 0. 
public class VariantTest 
{ 
    static void Main(string[] args) 
    { 
     Variant v = new Variant() { Integer = 2 }; 
     Console.WriteLine("{0}", v.Float); 

     Console.ReadLine(); 
    } 
} 

C++ variante del possono anche essere memorizzati sul mucchio, come ho osservato in precedenza. Se lo fai, probabilmente vuoi che la firma della memoria sia la stessa. Il modo per farlo è inserire la struttura Variant che abbiamo creato in precedenza semplicemente inserendola in object.

+0

@KenKin in realtà è solo una delle 4 opzioni che ho dato ... – atlaste

0

Facciamo un passo indietro. Prima o poi, vogliamo i dati effettivi nella VARIANTE. Un VARIANT è solo un detentore di dati significativi. Supponiamo di aver convertito il VARIANT in una sorta di oggetto in C# che aveva il tipo di variante e un buffer raw sotto .NET hood (ad esempio, le stringhe .NET possono esporre il buffer raw). A quel punto, il tipo VARIANT dovrebbe essere determinato dall'oggetto e i dati grezzi convertiti o convertiti nel tipo di dati specificato dalla variante e quindi creare un nuovo oggetto, ad es. stringa/int/etc. dai dati grezzi.

Quindi, piuttosto che preoccuparsi di passare il VARIANT al C#, guarda il tipo di dati variante e convertilo in C++ al tipo di dati effettivo e passa a C#.

Ad esempio, se il tipo di VARIANT è VT_INT, quindi ottenere l'int dalla variante e possono usare qualcosa come:

VARIANT var; 

Int^ returnInt = gcnew Int(var.intVal); 

returnInt può essere restituito come parametro di partenza da una funzione C++ in una DLL C++ che può essere chiamato da C#. La DLL di C++ deve usare l'opzione/clr.

funzione sarà simile: -

void ThisFunctionReturnsAnInt(Runtime::InteropServices::OutAttribute Int^ % returnIntValue) 
{ 

    VARIANT var; 
    Int^ returnInt = gcnew Int(var.intVal); 
} 

Può usare approccio simile per altri tipi di dati. È naturale, una VARIANT di VT_INT è proprio come un int, non è come se ci fosse qualche conversione importante in corso, stai solo prendendo il valore reale dalla VARIANTE nel momento in cui ti interessa come te sarebbe se passassi un valore intero dritto da C++ a C#. Avresti comunque bisogno di fare il gcnew comunque.

1

Quando .NET implementa un'interfaccia COM, basta uso VARIANT * invece.

Poi bypass smistamento sul lato ricevente NET utilizzando un IntPtr tipo a ricevere il puntatore.

public class ComVariant 
{ 
    [StructLayout(LayoutKind.Sequential)] 
    public struct Variant 
    { 
     public ushort vt; 
     public ushort wReserved1; 
     public ushort wReserved2; 
     public ushort wReserved3; 
     public Int32 data01; 
     public Int32 data02; 
    } 

    private Variant _variant; 

    private IntPtr _variantPtr; 

    public ComVariant(int variantPtr) : this(new IntPtr(variantPtr)) 
    { 
    } 

    public ComVariant(IntPtr variantPtr) 
    { 
     _variant = (Variant)Marshal.PtrToStructure(variantPtr, typeof(Variant)); 
     _variantPtr = variantPtr; 
    } 

    public VarEnum Vt 
    { 
     get 
     { 
      return (VarEnum)_variant.vt; 
     } 
     set 
     { 
      _variant.vt = (ushort)value; 
     } 
    } 

    public object Object 
    { 
     get 
     { 
      return Marshal.GetObjectForNativeVariant(_variantPtr); 
     } 
    } 
} 

poi se si accede a un puntamento VT_UNKNOWN a un'istanza oggetto interfaccia COM, solo

var variant = new ComVariant(variantPtr); 
var stream = variant.Object as IStream; // will not be null if type is correct 
var obj = variant.Object as IObj; // in general... 

farà il trucco, ma attenzione a non utilizzare un VARIANT appena allocato e dando la sua proprietà a l'implementazione .NET senza deallocarla da qualche parte ...

Per codice più complesso si potrebbe leggerethis article che parla anche di gestione della memoria.