shai_hulud, вообще задача сводится к тому что для серверного приложения нужно написать 200+ классов-пакетов. К каждому пакету соответственно методы Create (ObjToByteArray) и Parse (ByteArrayToObj). Мне не хотелось их писать, я думал, что можно кастить с помощью Marshal.StructureToPtr и Marshal.PtrToStructure из пространства имен System.Runtime.InteropServices. Я тут провел небольшой тест. Он конечно очень дилетантский, но уже позволил сделать выводы:
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public class TestClass {
[MarshalAs(UnmanagedType.I4)]
public Int32 _int32;
[MarshalAs(UnmanagedType.U4)]
public UInt32 _uint32;
[MarshalAs(UnmanagedType.I8)]
public Int64 _int64;
[MarshalAs(UnmanagedType.U8)]
public UInt64 _uint64;
public void func_testMarshal(Random rnd){
for(int i=0; i<10000000; i++){
TestClass test = new TestClass(){
_int32 = rnd.Next(1, 65535),
_int64 = rnd.Next(1, 65535),
_uint32 = (uint)rnd.Next(1, 65535),
_uint64 = (ulong)rnd.Next(1, 65535)
};
byte[] b = MarshalUtils.StructureToByteArray(test);
// имитация бурной деятельности
b[0] = 1;
}
}
Соответственно StructureToByteArray
public static byte[] StructureToByteArray(object data){
var size = Marshal.SizeOf(data);
var bytes = new byte[size];
var ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(data, ptr, false);
Marshal.Copy(ptr, bytes, 0, size);
Marshal.FreeHGlobal(ptr);
return bytes;
}
Time : 3442ms
А вот если рулить над этим всем руками.. банальным BitConverter.GetBytes и Buffer.BlockCopy
Влад, Задача понятна, я аналогичную проблему решил кодогенерацией и очень простым DSL. В проекте валяется файлы типа CancellationRaised.struct написанные на всевдо C#:
Далее Т4 шаблон находит все эти структ файлы и генерит по ним C# код со всем болейрплейтом:
[StructLayout(LayoutKind.Explicit, Size = 16, Pack = 1)]
public partial struct CancellationRaised
{
[FieldOffset(0)]
public Int64 MessageId;
[FieldOffset(8)]
public Int64 CancellationTokenId;
static CancellationRaised()
{
TypePacker<CancellationRaised>.Register<Serializer>();
}
public CancellationRaised(Int64 messageid, Int64 cancellationtokenid)
{
this.MessageId = messageid;
this.CancellationTokenId = cancellationtokenid;
}
public override int GetHashCode()
{
return unchecked
(
(this.MessageId == default(Int64) ? 0 : this.MessageId.GetHashCode()) +
(this.CancellationTokenId == default(Int64) ? 0 : this.CancellationTokenId.GetHashCode())
);
}
public override bool Equals(object value)
{
return value is CancellationRaised && this.Equals((CancellationRaised)value);
}
public bool Equals(CancellationRaised other)
{
return
(
object.Equals(this.MessageId, other.MessageId) &&
object.Equals(this.CancellationTokenId, other.CancellationTokenId)
);
}
public override string ToString()
{
return
(
"CancellationRaised, " +
"MessageId: " + this.MessageId + ", " +
"CancellationTokenId: " + this.CancellationTokenId
);
}
public sealed class Serializer : BlittableTypePacker<CancellationRaised>
{
public override int Write(ref CancellationRaised value, WriteBuffer writeBuffer)
{
writeBuffer.EnsureRange(16);
var buffer = writeBuffer.Buffer;
unsafe
{
fixed (byte* b = buffer)
*((CancellationRaised*)(b + writeBuffer.Offset)) = value;
}
writeBuffer.Offset += 16;
writeBuffer.BytesAvailable -= 16;
return 16;
}
public override int Read(out CancellationRaised value, ReadBuffer readBuffer)
{
readBuffer.EnsureRange(16);
var buffer = readBuffer.Buffer;
unsafe
{
fixed (byte* b = buffer)
value = *((CancellationRaised*)(b + readBuffer.Offset));
}
readBuffer.Offset += 16;
readBuffer.BytesAvailable -= 16;
return 16;
}
public override int Measure(ref CancellationRaised value)
{
return 16;
}
}
}
В итоге надо только описывать новые пакеты, а код оптимальной сериализации генерируется сам. Для non-blittable типов надо писать кастомные "сериализаторы". У меня написано для всего что я использую типа string, Version, decimal итд. Этим кодом не поделюсь.
П.с. все blittable структуры типа вашей быстрее всего копировать по поинтеру, а не маршалингом.