Тип данных варианта C++ в C#
что эквивалентно вариантному типу данных C++ в C#?
У меня есть код на C++, который использует тип данных VARIANT. Как я могу преобразовать этот код в C#?
4 ответов
Это сложный вопрос.
из C# 4, Вы можете использовать динамический, чтобы указать, что тип известен во время выполнения.
по моему личному пониманию, однако, c++ требует тип известен во время компиляции. Таким образом, вы можете использовать object
, а object
в C# является существующим типом.
концепции multi-type, одно значение (он же полиморфизм) варианта, вам не нужно будет найти соответствующий тип в C#, просто определите свои классы и интерфейсы. Вы всегда можете ссылаться на объект как на его интерфейс, который реализует класс.
Если вы портируете код, и чтобы выяснить синтаксис, который вы можете просто использовать в LHS, и для рассмотрения типа известно во время компиляции, используйте var.
ну, на самом деле есть два варианта в C++: boost::variant и COM variant. Решение следует более или менее той же идее, но первое более сложное. Полагаю, вы собираетесь использовать последнее.
позвольте мне начать, сказав, что это то, что вы просто не должны использовать, если это возможно. Тем не менее, вот как вы это делаете: -)
варианты и взаимодействие
варианты иногда используются во взаимодействии, если вам нужен байт представление должно быть одинаковым.
если вы имеете дело с interop, не забудьте проверить VariantWrapper
класс на MSDN и заставить его работать так.
варианты и соображения переноса
варианты в основном используются в API, и обычно так:
void Foo(SomeEnum operation, Variant data);
причина, по которой это делается в C++, заключается в том, что нет базы object
класса и поэтому вам нужно что-то вроде этого. Самый простой способ портировать это - изменить подпись на:
void Foo(SomeEnum operation, object data);
однако, если вы все равно портируете, вы также серьезно хотите рассмотреть эти два, так как они разрешаются во время компиляции и могут сохранить вам большой "переключатель", который обычно следует в методе Foo
:
void SomeOperation(int data);
void SomeOperation(float data);
// etc
варианты и последовательность байтов
в редких случаях вам нужно манипулировать самими байтами.
по существу, вариант-это просто большое объединение типов значений, завернутых в один тип значения (структуры). В C++ можно выделить тип значения в куче, потому что структура такая же, как класс (ну почти). Как используется тип значения, это немного важно, но об этом позже.
Союз просто означает, что вы собираетесь перекрывать все данные в памяти. Обратите внимание, как я явно отметил тип значения выше; для variant это в основном то, о чем речь. Это также дает нам способ проверить его-а именно, проверив другое значение в структура.
способ сделать это в C# - использовать в тип значения, который в основном работает следующим образом:
[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++ также может быть сохранен в куче, как я уже отмечал ранее. Если вы это сделаете, вы, вероятно, все еще хотите, чтобы подпись памяти была такой же. Способ сделать это-упаковать структуру варианта, которую мы строим ранее, просто обрамляя ее в object
.
когда .NET реализует COM-интерфейс, просто использовать вариант*.
затем обход сортировочных на стороне получения .NET с помощью IntPtr введите для получения указателя.
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);
}
}
}
тогда, если вы обращаетесь к VT_UNKNOWN, указывающему на экземпляр объекта com-интерфейса, просто
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...
будет делать трюк, но обратите внимание, чтобы не использовать вновь выделенный вариант и дать его право собственности реализация .NET, не освобождая ее где-то...
для более сложного кода вы можете ознакомиться в этой статье что также говорит об управлении памятью.
давайте сделаем шаг назад. Рано или поздно нам понадобятся фактические данные в варианте. Вариант - это просто держатель значимых данных. Предположим, мы преобразовали вариант в какой-то объект в C#, который имел тип variant и некоторый необработанный буфер под капотом .NET (например, строки .NET могут предоставить необработанный буфер). В этот момент тип VARIANT должен быть определен из объекта и необработанных данных, преобразованных или приведенных к типу данных, указанному variant, а затем создать новый объект например, string / int / etc. из исходных данных.
поэтому, вместо того, чтобы беспокоиться о передаче варианта на C#, посмотрите на тип данных variant и преобразуйте его в C++ в фактический тип данных и передайте его на C#.
например, если тип VARIANT-VT_INT, то получите int из variant и можете использовать что-то вроде:
VARIANT var;
Int^ returnInt = gcnew Int(var.intVal);
returnInt может быть возвращен как параметр Out из функции C++ в dll C++, который может быть вызван из C#. Dll C++ должна использовать /clr выбор.
функция будет выглядеть:-
void ThisFunctionReturnsAnInt(Runtime::InteropServices::OutAttribute Int^ % returnIntValue)
{
VARIANT var;
Int^ returnInt = gcnew Int(var.intVal);
}
можно использовать аналогичный подход и для других типов данных. Это естественно, вариант VT_INT действительно похож на int, это не так, как будто происходит какое-то крупное преобразование, вы просто берете фактическое значение из варианта в то время, когда вы заинтересованы в нем, как если бы вы передавали прямое целочисленное значение из C++ в C#. Вам все равно нужно будет сделать gcnew в любом случае.