2013-04-04 11 views
8

Was entspricht dem VARIANT-Datentyp von C++ in C#?VARIANT-Datentyp von C++ in C#

Ich habe Code in C++, der den VARIANT-Datentyp verwendet. Wie kann ich diesen Code in C# konvertieren?

+0

Müssen Sie mit C++ - Code interoperieren oder nur Code konvertieren? – Botz3000

+3

Das direkte Äquivalent ist 'Objekt' als Typ und' object.GetType() 'als das" Was ist in der Variante? " Informationen, aber das würde für wirklich schlechte C# -Code machen. Der Kontext ist wichtig. – Jon

+0

Wenn Sie C# 4.0 verwenden, können Sie den 'dynamischen' Datentyp verwenden, aber wie @Jon sagt, wird dies für einen schlechten C# -Code sorgen, da Sie type safty und die Überprüfung der Kompilierzeit umgehen. –

Antwort

1

Dies ist eine knifflige Frage.

Von C# 4 können Sie dynamische verwenden, um anzugeben, dass der Typ zur Laufzeit bekannt ist.

Nach meinem persönlichen Verständnis benötigt C++ jedoch den zum Zeitpunkt der Kompilierung bekannten Typ. Daher könnten Sie in Betracht ziehen, object zu verwenden, aber object in C# ist ein vorhandener Typ.

Für das Konzept von Multi-Typ, Einzelwert (AKA Polymorphismus) von VARIANT, müssten Sie nicht einen entsprechenden Typ in C# finden, nur Ihre Klassen und Schnittstellen definieren. Sie können immer auf ein Objekt als seine Schnittstelle verweisen, die die Klasse implementiert.

Wenn Sie den Code portieren und eine Syntax ermitteln, die Sie einfach in LHS verwenden können und die Berücksichtigung des Typs zur Kompilierungszeit bekannt ist, verwenden Sie var.

7

Nun, es gibt tatsächlich zwei Varianten in C++: boost :: variant und COM-Variante. Die Lösung folgt mehr oder weniger der gleichen Idee, aber ersteres ist komplexer. Ich nehme an, Sie wollen das letztere benutzen.

Lassen Sie mich zuerst beginnen, indem Sie sagen, dass dies etwas ist, das Sie nicht verwenden sollten, wenn möglich. Das heißt, das ist, wie Sie es tun :-)

Varianten und Interop

Varianten sind manchmal in Interop von verwendet, wenn Sie die Byte-Darstellung müssen gleich sein.

Wenn Sie mit Interop beschäftigen, stellen Sie sicher, die Klasse auf MSDN zu überprüfen und machen es so.

Varianten und Portierung Überlegungen

Varianten sind meist in APIs verwendet werden, und in der Regel wie folgt aus:

void Foo(SomeEnum operation, Variant data); 

Der Grund ist es wie dies in C getan ++ ist, weil es keine Basis object Klasse und Du brauchst also so etwas. Der einfachste Weg, dies zu tragen ist, die Signatur zu ändern:

void Foo(SomeEnum operation, object data); 

Wenn Sie jedoch trotzdem portieren, auch Sie ernsthaft, diese beiden betrachten wollen, da sie zum Zeitpunkt der Kompilierung aufgelöst werden und können Sie sparen der große ‚Schalter‘, die in der Regel in der Methode folgt Foo:

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

Varianten und Bytekonsistenz

in seltenen Fällen müssen Sie das Bytes selbst manipulieren.

Im Wesentlichen ist die Variante nur eine große Union von Werttypen, die in einem einzigen Werttyp (struct) eingeschlossen sind. In C++ können Sie dem Heap einen Werttyp zuordnen, da eine Struktur die gleiche ist wie eine Klasse (gut sortiert). Wie der Werttyp verwendet wird, ist nur ein bisschen wichtig, aber dazu später mehr.

Union bedeutet einfach, dass Sie alle Daten im Speicher überlappen. Beachten Sie, wie ich den Werttyp oben explizit notiert habe. Für Varianten ist das im Grunde genommen das, worum es geht. Dies gibt uns auch eine Möglichkeit, es zu testen - nämlich einen anderen Wert in der Struktur zu überprüfen.

Der Weg, dies in C# zu tun ist, das StructLayout Attribut in einem Werttyp zu verwenden, die im Grunde funktioniert wie folgt:

[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 ist auch auf dem Heap gespeichert werden kann, wie ich bereits erwähnt. Wenn Sie dies tun, möchten Sie wahrscheinlich immer noch, dass die Speichersignatur identisch ist. Der Weg, dies zu tun, besteht darin, die Variant-Struktur, die wir früher gebaut haben, zu boxen, indem man sie einfach in object einfügt.

+0

@KenKin das ist eigentlich nur eine der 4 Optionen, die ich gab ... – atlaste

0

Lassen Sie uns einen Schritt zurückgehen. Früher oder später wollen wir die tatsächlichen Daten im VARIANT. Ein VARIANT ist nur ein Halter für aussagekräftige Daten. Angenommen, wir haben die VARIANT in eine Art Objekt in C# konvertiert, das den Variant-Typ und einen rohen Puffer unter der .NET-Haube hatte (z. B. können .NET-Strings den Raw-Puffer freilegen). An diesem Punkt müsste der VARIANT-Typ aus dem Objekt und den Rohdaten bestimmt werden, die in den durch die Variante spezifizierten Datentyp umgewandelt oder umgewandelt werden, und dann ein neues Objekt, z. string/int/etc. aus den Rohdaten.

Anstatt sich darum zu sorgen, dass die VARIANT an C# übergeben wird, sehen Sie sich den Variant-Datentyp an und konvertieren ihn in C++ in den tatsächlichen Datentyp und übergeben ihn an C#.

Zum Beispiel, wenn der Typ VARIANT VT_INT ist, erhalten dann die int von der Variante und so etwas wie verwenden:

VARIANT var; 

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

returnInt kann als Out-Parameter aus einer C++ Funktion in einem C++ DLL zurückgegeben werden das kann von C# aus aufgerufen werden. Die C++ - DLL muss die Option/clr verwenden.

Funktion würde wie folgt aussehen: -

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

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

ähnlichen Ansatz für andere Datentypen verwenden können. Es ist nur natürlich, eine VARIANT von VT_INT ist wirklich genau wie ein int, es ist nicht so, als ob es eine große Umwandlung gibt, du nimmst nur den tatsächlichen Wert aus der VARIANT zu der Zeit, wenn du daran interessiert bist wie du würde, wenn Sie einen geraden Ganzzahlwert von C++ nach C# übergeben würden. Sie müssten das gcnew trotzdem tun.

1

Wenn .NET implementiert eine COM-Schnittstelle, nur verwenden VARIANT * stattdessen.

Dann Bypass auf .NET Empfangsseite Rangier unter Verwendung eines IntPtr Typ den Zeiger zu empfangen.

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); 
     } 
    } 
} 

dann, wenn Sie eine VT_UNKNOWN die auf eine COM-Schnittstelle Objektinstanz zugreifen, nur

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

den Trick tun, aber darauf achten, nicht ein neu zugewiesenen VARIANT zu verwenden und geben das Eigentum an die .NET-Implementierung, ohne sie irgendwo freizugeben ...

Für komplexeren Code können Sie lesenthis article, die auch über Speicherverwaltung spricht.