C# BinaryReader反序列化二维数组时数据全部存入单行问题排查
问题根因
两个异常(数组维度变成[42,1]、读取到的columns值为70000000000000这类离谱值)本质都是序列化与反序列化的读写顺序/内容不匹配,和BinaryWriter/Reader、二维数组本身的循环读写逻辑无关。
- 你贴出的
Serialize方法根本没有写入rows和columns字段,上来直接循环写入了6*7=42个棋盘格的int值。只要反序列化时尝试先读行列值,读到的就是棋盘内容的前几个字节,甚至是流中更靠前的类名字符串的残留字节,所有后续读操作的偏移量全错,自然会读出乱码值。 - 你拿到的[42,1]维度数组,根本不是类中初始化的那个
board。你在类里明确定义了board初始化为new int[6,7],贴出的反序列化逻辑也是硬编码行列值往这个数组里填值,不可能凭空变维度。这说明你在Deserialize方法执行完之后,又额外从流里读了一次数组:大概率是你给所有数组写了通用读取逻辑,默认先读两个int当维度,再读对应数量的元素,这时候流的位置已经在42个棋盘值的末尾或者中间,读出来的维度值完全是无效数据,自然会创建出维度错误的数组。 - 额外排查点:如果写完数据后用
MemoryStream.GetBuffer()取字节数组,也会出问题。这个方法返回的是内存流的内部预留缓冲区,长度比实际写入的有效内容长,多余位置是内存残留值,会直接导致读取错位。
修复方案
严格保证序列化和反序列化的读写顺序、读写内容完全一一对应,不要硬编码固定值,也不要在序列化方法外额外读取对象内容:
- 修正
Connect4BoardData的序列化/反序列化逻辑,把行列值按固定顺序写入流,反序列化时按相同顺序读取,不要硬编码6、7的固定值:
public override void Serialize(Packet pPacket) { // 先写行列数,顺序固定 pPacket.Write(rows); pPacket.Write(columns); // 再循环写入棋盘数据 for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { pPacket.Write(board[i, j]); } } } public override void Deserialize(Packet pPacket) { // 读取顺序和写入完全一致,先读行列 rows = pPacket.ReadInt(); columns = pPacket.ReadInt(); // 按读取到的大小初始化数组,不要用声明时写死的维度 board = new int[rows, columns]; // 再循环读取棋盘内容 for (int i = 0; i < rows; i++) { for (int j = 0; j < columns; j++) { board[i, j] = pPacket.ReadInt(); } } }
- 检查Packet类的字节提取逻辑:写完数据后从MemoryStream取有效字节,必须用
ToArray()方法,禁止用GetBuffer()。 - 删掉
ReadObject()方法返回对象后,额外从Packet流中读取数组的逻辑。棋盘数据的反序列化已经在Deserialize方法内完成,直接取返回实例的board字段即可,额外读取只会打乱流的位置指针。
验证方式
改完后可以断点核对:
- 序列化后的总字节长度 = 类名字符串写入长度 + 2个int(行列,共8字节) + 42个int(棋盘数据,共168字节),和实际拿到的字节数组长度一致
- 反序列化读类名完成后,第一个读到的int是6,第二个是7,后续42个int和写入的棋盘值完全对应
- 返回实例的
board调用GetLength(0)返回6、GetLength(1)返回7,不会再抛索引越界异常
内容的提问来源于stack exchange,提问作者Dinnea
相关产品推荐
相关产品推荐

