У меня такой сценарий:
public class Test
{
public void main()
{
//arrange
var a = new A(4,2);
var b = new B(3, 6, 6, 4);
//act
var a1 = a.ObjectToByteArray();
var b1 = b.ObjectToByteArray();
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 3)]
public class A
{
[MarshalAs(UnmanagedType.U1, SizeConst = 1)]
byte a1;
[MarshalAs(UnmanagedType.U1, SizeConst = 1)]
byte a2;
[MarshalAs(UnmanagedType.U1, SizeConst = 1)]
byte a3;
public A(byte a1, byte a2)
{
this.a1 = a1;
this.a2 = a2;
this.a3 = 9;
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 6)]
public class B : A
{
[MarshalAs(UnmanagedType.U1, SizeConst = 1)]
byte b1;
[MarshalAs(UnmanagedType.U1, SizeConst = 1)]
byte b2;
[MarshalAs(UnmanagedType.U1, SizeConst = 1)]
byte b3;
public B(byte a1, byte a2, byte b1, byte b2) : base(a1, a2)
{
this.b1 = b1;
this.b2 = b2;
this.b3 = 8;
}
}
}
А затем метод расширения:
public static byte[] ObjectToByteArray<T>(this T source)
{
int rawSize = Marshal.SizeOf(typeof(T));
var rawData = new byte[rawSize];
//var h1 = GCHandle.Alloc(source, GCHandleType.Pinned);
//var ptr = h1.AddrOfPinnedObject();
//h1.Free();
var handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
Marshal.StructureToPtr(source, handle.AddrOfPinnedObject(), false);
handle.Free();
return rawData;
}
Размер первого экземпляра типа A (a) определяется функцией Marshal.SizeOf(typeof(T))
и является правильным (3 байта), но когда in приходит ко второму экземпляру типа B (b), та же функция возвращает 7 вместо 6 байтов.
Может ли кто-нибудь объяснить, что я делаю неправильно, или это ожидаемое поведение?
Заранее спасибо, Юлиан
Также важно отметить, что здесь вы указываете способ маршал класса. Это нет размер классов в (управляемой) памяти, который игнорирует всю эту упаковку и компоновку. Маршаллинг - не обязательно самый удобный способ просто отобразить определенную последовательность байтов; его следует использовать только для взаимодействия с неуправляемым кодом.
@JeroenMostert все, что вы предложили, верно, это сработало, оставив атрибут MarshalAs
. Одно уточнение: если в классе есть члены массива, они должны быть с атрибутом MarshalAs
? Я правильно понял?
Если вы не укажете MarshalAs
, член массива маршалируется как указатель, что, вероятно, не то, что вам нужно. Чтобы маршалировать массив как встроенную последовательность значений, вы должны использовать MarshalAs(UnmanagedType.ByValArray, SizeConst = ...)
. В этом случае размер должен быть постоянным, поскольку маршалинг по умолчанию не может обрабатывать ничего более сложного.
На моей машине это дает ожидаемый результат (в 64-битной среде выполнения), полностью исключая
Size
. Похоже, что маршаллер не принимает во внимание наследование при оценкеSize
. Вы должны оставить его в любом случае, если только вы не намерены дополнить результат, так как маршаллер игнорирует этот атрибут, если вы пытаетесь сделать размер меньше минимального. Кроме того, ни один из атрибутовMarshalAs
не является обязательным, поскольку вы просто указываете маршаллинг по умолчанию для байтов (аSizeConst
игнорируется, поскольку он актуален только для массивов). Не переоценивайте вещи.