首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 文件流讀寫結構體(二):Marshal 序列化完整實現

     編輯:關於C語言

核心思路

利用 System.Runtime.InteropServices.Marshal 將結構體與字節數組互轉,再通過 FileStream 讀寫文件。關鍵在於結構體佈局屬性與字段封送屬性的精確定義。

結構體定義


[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TestStruct
{
    [MarshalAs(UnmanagedType.R8)]      // double
    public double dTest;

    [MarshalAs(UnmanagedType.U2)]      // UInt16
    public ushort uTest;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] // 固定長度 byte[]
    public byte[] bTest;
}

序列化與反序列化


// 結構體 → byte[]
public static byte[] RawSerialize(object obj)
{
    int size = Marshal.SizeOf(obj);
    IntPtr ptr = Marshal.AllocHGlobal(size);
    try
    {
        Marshal.StructureToPtr(obj, ptr, false);
        byte[] bytes = new byte[size];
        Marshal.Copy(ptr, bytes, 0, size);
        return bytes;
    }
    finally { Marshal.FreeHGlobal(ptr); }
}

// byte[] → 結構體
public static TestStruct RawDeserialize(byte[] bytes)
{
    int size = Marshal.SizeOf(typeof(TestStruct));
    if (size > bytes.Length) return default;

    IntPtr ptr = Marshal.AllocHGlobal(size);
    try
    {
        Marshal.Copy(bytes, 0, ptr, size);
        return (TestStruct)Marshal.PtrToStructure(ptr, typeof(TestStruct));
    }
    finally { Marshal.FreeHGlobal(ptr); }
}

WinForm 測試界面


private void BtnWrite_Click(object sender, EventArgs e)
{
    string path = Application.StartupPath + @"\test.dat";
    using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write))
    {
        var ts = new TestStruct
        {
            dTest = double.Parse(txtDouble.Text),
            uTest = ushort.Parse(txtUshort.Text),
            bTest = Encoding.Default.GetBytes(txtString.Text)
        };
        fs.Write(RawSerialize(ts), 0, Marshal.SizeOf(ts));
    }
}

private void BtnRead_Click(object sender, EventArgs e)
{
    string path = Application.StartupPath + @"\test.dat";
    if (!File.Exists(path)) { MessageBox.Show("文件不存在"); return; }

    byte[] bytes = new byte[Marshal.SizeOf(typeof(TestStruct))];
    using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
        fs.Read(bytes, 0, bytes.Length);

    var ts = RawDeserialize(bytes);
    txtDouble.Text = ts.dTest.ToString();
    txtUshort.Text = ts.uTest.ToString();
    txtString.Text = Encoding.Default.GetString(ts.bTest);
}

注意事項與避坑指南

適用場景

  • 與 C/C++ 共享二進制文件格式(如嵌入式設備通訊、舊系統數據遷移)。
  • 高性能本地緩存,結構體固定、讀寫頻繁。
  • 不適用於:跨語言、跨平台、版本演進頻繁的數據交換。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
問題原因解決
字段偏移錯誤結構體對齊與字段順序不匹配使用 LayoutKind.Sequential,按聲明順序;必要時用 FieldOffset 顯式指定
字符串亂碼CharSet 不一致(Ansi/Unicode)結構體與 MarshalAs 統一 CharSet,或改用固定長度 byte[] 手動編碼
數組長度不固定ByValArray 需 SizeConst確定最大長度,讀取後按實際有效長度解碼
跨平台失效結構體大小/對齊在 32/64 位不同避免在跨平台持久化場景使用,改用 Protobuf/JSON/BinaryFormatter