二進制格式在效能、空間、跨語言互通上具優勢。C# 透過 BinaryReader/Writer、Marshal、StructLayout 解決對齊、位元序、型別轉換挑戰。
| 挑戰 | C/C++ 做法 | C# 解決方案 |
|---|---|---|
| 結構記憶體配置 | 編譯器自動處理 | StructLayout 屬性控制 |
| 位元序 (Endianness) | 手動位元組交換 | 反射自動檢測/轉換 |
| 型別安全讀寫 | 指標強制轉型 | BinaryReader/Marshal |
| 複雜型別 (字串/陣列) | 固定大小緩衝區 | MarshalAs 指定編碼 |
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MyData {
public int Id;
public short Status;
public byte Flags;
// Pack=1 緊密封裝,無對齊填充
}
LayoutKind.Sequential:依宣告順序配置LayoutKind.Explicit:配合 FieldOffset 精確控制Pack:對齊邊界 (1, 2, 4, 8, 16),1=緊密[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct FileHeader {
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
public string FileName; // 固定 50 字元 ANSI 字串
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public byte[] Reserved; // 固定大小陣列
}
.NET 執行階段可存取型別中繼資料,實作通用位元序轉換:
public static void FixEndianness(object obj) {
foreach (FieldInfo field in obj.GetType().GetFields()) {
if (field.FieldType == typeof(ushort) || field.FieldType == typeof(uint)) {
object val = field.GetValue(obj);
if (val is ushort u16) field.SetValue(obj, Swap(u16));
else if (val is uint u32) field.SetValue(obj, Swap(u32));
}
// 遞迴處理巢狀結構...
}
}
static ushort Swap(ushort v) => (ushort)((v >> 8) | (v << 8));
static uint Swap(uint v) => (v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | (v << 24);
注意:浮點數通常遵循 IEEE 754,不需交換;負數補數表示法跨平台一致。
// 寫入
using (var fs = new FileStream("data.bin", FileMode.Create))
using (var bw = new BinaryWriter(fs, Encoding.UTF8)) {
bw.Write(123); // int
bw.Write(3.14f); // float
bw.Write("Hello"); // 字串 (長度前綴)
bw.Write(new byte[10]); // 位元組陣列
}
// 讀取
using (var fs = new FileStream("data.bin", FileMode.Open))
using (var br = new BinaryReader(fs, Encoding.UTF8)) {
int i = br.ReadInt32();
float f = br.ReadSingle();
string s = br.ReadString();
byte[] buf = br.ReadBytes(10);
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Particle {
public float X, Y, Z;
public float Vx, Vy, Vz;
public int Life;
}
public static Particle[] ReadParticles(string path) {
byte[] data = File.ReadAllBytes(path);
int count = data.Length / Marshal.SizeOf();
var particles = new Particle[count];
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
try {
IntPtr ptr = handle.AddrOfPinnedObject();
for (int i = 0; i < count; i++) {
particles[i] = Marshal.PtrToStructure(ptr);
ptr = IntPtr.Add(ptr, Marshal.SizeOf());
}
} finally {
handle.Free();
}
return particles;
}
| 方法 | 速度 | 記憶體 | 適用場景 |
|---|---|---|---|
| BinaryReader/Writer | 中 | 低 (串流) | 一般檔案、網路傳輸 |
| Marshal 結構映射 | 極快 | 高 (全載入) | 大量同質結構、遊戲資產 |
| MemoryMappedFile | 極快 | 共享記憶體 | 超大檔案、進程間通訊 |
C# 雖有托管限制,但結合StructLayout、MarshalAs、BinaryReader、MemoryMappedFile,完全能勝任高效能二進制處理,且比 C/C++ 更型別安全、更易維護。