我有一个用Pack = 1定义的结构,它长29个字节.如果它没有打包,它将是32个字节长.
> Marshal.SizeOf(TypeOf(StructName))返回29.
> StructName结构; sizeof(struct)返回32.
当我使用MemoryMappedViewAccessor写出该结构时,它写出32个字节,而不是29个字节.
因此,如果没有将结构编组到一个字节数组并以这种方式写出来,有没有办法让它正确写出该结构?
更多细节:如果使用显式布局,Write实际上会写出29个字节.但是,WriteArray为每个元素写出32个字节.
而avip,是的,细致的字节序列化可能会起作用,但是(我没有对它进行分析,但我猜)它可能比WriteArray慢几个数量级,不是吗?
解决方法
编辑:好的,我终于明白了你真正的要求.我们通常不使用MemoryMappedViewAccessor来序列化对象,现在你知道为什么了.
以下内容将为您提供预期的结果.
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,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); } } }
编辑:所以似乎DateTime真的喜欢在对齐的内存地址上.虽然您可以定义显式布局,但我认为更简单的方法是:
[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; } } }
虽然我不明白为什么你应该关心托管内存表示.
或者,[StructLayout(LayoutKind.Explicit)]应该阻止内存对齐.
示例(‘托管sizeof’取自this post)
[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"); } } }
输出:
Managed size: 9 Unmanaged size: 9 |255|63|55|244|117|40|202|43|42| // managed memory layout is as expected
BTW,DateTime似乎打破了顺序合同 – 但请记住合同是用于Marshaled内存映射.关于托管内存布局没有规范.
[StructLayout(LayoutKind.Sequential,Pack = 1,Size = 9)] public struct Test { public DateTime dt1; public byte b; }
Managed size: 12 Unmanaged size: 9 |42|0|0|0|255|63|55|244|117|40|202|43| // finally found those 3 missing bytes :-)