Modifier : OK, j'ai finalement compris ce que vous demandez vraiment. Nous n'utilisons généralement pas MemoryMappedViewAccessor
pour sérialiser des objets, et maintenant vous savez pourquoi.
Ce qui suit vous donnera le résultat escompté.
public static class ByteSerializer
{
public static Byte[] Serialize<T>(IEnumerable<T> msg) where T : struct
{
List<byte> res = new List<byte>();
foreach (var s in msg)
{
res.AddRange(Serialize(s));
}
return res.ToArray();
}
public static Byte[] Serialize<T>(T msg) where T : struct
{
int objsize = Marshal.SizeOf(typeof(T));
Byte[] ret = new Byte[objsize];
IntPtr buff = Marshal.AllocHGlobal(objsize);
Marshal.StructureToPtr(msg, buff, true);
Marshal.Copy(buff, ret, 0, objsize);
Marshal.FreeHGlobal(buff);
return ret;
}
}
class Program
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Yours
{
public Int64 int1;
public DateTime dt1;
public float f1;
public float f2;
public float f3;
public byte b;
}
static void Main()
{
var file = @"c:\temp\test.bin";
IEnumerable<Yours> t = new Yours[3];
File.WriteAllBytes(file, ByteSerializer.Serialize(t));
using (var stream = File.OpenRead(file))
{
Console.WriteLine("file size: " + stream.Length);
}
}
}
EDITAR : Il semble donc que DateTime aime vraiment être sur une adresse mémoire alignée. Bien que vous puissiez définir la disposition explicite, je pense qu'une approche plus simple serait :
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Test
{
private long dt1;
public byte b;
public Int64 int1;
public float f1;
public float f2;
public float f3;
public DateTime DT
{
get { return new DateTime(dt1); }
set { dt1 = value.Ticks; }
}
}
Mais je ne vois pas pourquoi vous devriez vous soucier de la représentation de la mémoire gérée.
Alternativement, [StructLayout(LayoutKind.Explicit)]
devrait empêcher l'alignement de la mémoire.
Exemple (l'expression "taille gérée" est tirée du document suivant ce poste )
[StructLayout(LayoutKind.Explicit, Size = 9)]
public struct Test
{
[FieldOffset(0)]
public DateTime dt1;
[FieldOffset(8)]
public byte b;
}
class Program
{
static readonly Func<Type, uint> SizeOfType = (Func<Type, uint>)Delegate.CreateDelegate(typeof(Func<Type, uint>), typeof(Marshal).GetMethod("SizeOfType", BindingFlags.NonPublic | BindingFlags.Static));
static void Main()
{
Test t = new Test() { dt1 = DateTime.MaxValue, b = 42 };
Console.WriteLine("Managed size: " + SizeOfType(typeof(Test)));
Console.WriteLine("Unmanaged size: " + Marshal.SizeOf(t));
using (MemoryMappedFile file = MemoryMappedFile.CreateNew(null, 1))
using (MemoryMappedViewAccessor accessor = file.CreateViewAccessor())
{
accessor.Write(0L, ref t);
long pos = 0;
for (int i = 0; i < 9; i++)
Console.Write("|" + accessor.ReadByte(pos++));
Console.Write("|\n");
}
}
}
Sortie :
Managed size: 9
Unmanaged size: 9
|255|63|55|244|117|40|202|43|42| // managed memory layout is as expected
BTW, DateTime semble rompre le contrat séquentiel - mais n'oubliez pas que le contrat concerne la carte mémoire Marshaled. Il n'y a aucune spécification concernant la disposition de la mémoire gérée.
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 9)]
public struct Test
{
public DateTime dt1;
public byte b;
}
Et la sortie du code ci-dessus :
Managed size: 12
Unmanaged size: 9
|42|0|0|0|255|63|55|244|117|40|202|43| // finally found those 3 missing bytes :-)