7

で定義された構造体があり、Pack=1長さは 29 バイトです。パックされていない場合、長さは 32 バイトになります。

  • Marshal.SizeOf(TypeOf(StructName))29 を返します。

  • StructName struct; sizeof(struct)32 を返します。

それを使用してその構造体を書き出すとMemoryMappedViewAccessor、29バイトではなく32バイトが書き出されます。

したがって、構造体をバイト配列にマーシャリングしてそのように書き出す以外に、その構造体を正しく書き出す方法はありますか?

詳細: Explicit レイアウトを使用する場合、Write は実際には 29 バイトを書き出します。ただし、WriteArray は要素ごとに 32 バイトを書き出します。

そしてavip、はい、細心の注意を払ったバイトのシリアル化はおそらく機能しますが、(プロファイルは作成していませんが推測していますが)おそらく WriteArray よりも桁違いに遅いですよね?

4

1 に答える 1

6

編集:わかりました、あなたが本当に求めていることをようやく理解しました。通常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, 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);
        }
    }
}

EDIT : したがって、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」はこの投稿から取得されます)

[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

ところで、DateTime はシーケンシャル コントラクトを破っているようですが、コントラクトはマーシャリングされたメモリ マップ用であることを忘れないでください。マネージ メモリのレイアウトに関する仕様はありません。

[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 :-)
于 2012-11-10T07:04:29.073 に答える