c# – 使用MemoryMappedViewAccessor错误编写打包的结构?

前端之家收集整理的这篇文章主要介绍了c# – 使用MemoryMappedViewAccessor错误编写打包的结构?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有一个用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 :-)
原文链接:https://www.f2er.com/csharp/244456.html

猜你在找的C#相关文章