C# BitMatrix类在Mono编译环境下触发StackOverflowException问题
问题:Mono环境下BitMatrix类触发StackOverflowException
我编写的C# BitMatrix类在Windows 11的VS Code(最新C#环境)中运行正常,但在兼容C#7的Linux Mono测试环境中,执行以下代码时抛出堆栈溢出异常:
// 同一对象的Equals调用 var m1 = new BitMatrix(5, 6); var m2 = m1; Console.WriteLine(m1.Equals(m2));
报错指向BitMatrix.op_Equality方法,具体错误信息如下:
未处理的异常: StackOverflowException [ERROR] 致命未处理异常: System.StackOverflowException: 请求的操作导致堆栈溢出。 在 BitMatrix.op_Equality (BitMatrix matrix1, BitMatrix matrix2) <0x40c71820 + 0x00004> in <ee17de3584e0451aa17782759cde6afe>:0
BitMatrix类完整代码:
// BitMatrix.cs using System; using System.Collections; using System.Collections.Generic; public class BitMatrix : IEquatable<BitMatrix> { private BitArray data; public int NumberOfRows { get; } public int NumberOfColumns { get; } public bool IsReadOnly => false; public BitMatrix(int numberOfRows, int numberOfColumns, params int[] bits) { if (numberOfRows < 1 || numberOfColumns < 1) throw new ArgumentOutOfRangeException("Incorrect size of matrix"); data = new BitArray(numberOfRows * numberOfColumns, false); if (bits != null) { for (int i = 0; i < bits.Length && i < data.Length; i++) { if (bits[i] != 0) data[i] = true; } } NumberOfRows = numberOfRows; NumberOfColumns = numberOfColumns; } public BitMatrix(int[,] bits) { if (bits == null) throw new NullReferenceException("bits cannot be null"); int numberOfRows = bits.GetLength(0); int numberOfColumns = bits.GetLength(1); if (numberOfRows < 1 || numberOfColumns < 1) throw new ArgumentOutOfRangeException("Incorrect size of matrix"); data = new BitArray(numberOfRows * numberOfColumns, false); for (int row = 0; row < numberOfRows; row++) { for (int col = 0; col < numberOfColumns; col++) { if (bits[row, col] != 0) data[row * numberOfColumns + col] = true; } } NumberOfRows = numberOfRows; NumberOfColumns = numberOfColumns; } public BitMatrix(bool[,] bits) { if (bits == null) throw new NullReferenceException("bits cannot be null"); int numberOfRows = bits.GetLength(0); int numberOfColumns = bits.GetLength(1); if (numberOfRows < 1 || numberOfColumns < 1) throw new ArgumentOutOfRangeException("Incorrect size of matrix"); data = new BitArray(numberOfRows * numberOfColumns, false); for (int row = 0; row < numberOfRows; row++) { for (int col = 0; col < numberOfColumns; col++) { if (bits[row, col]) data[row * numberOfColumns + col] = true; } } NumberOfRows = numberOfRows; NumberOfColumns = numberOfColumns; } public static int BoolToBit(bool boolValue) => boolValue ? 1 : 0; public static bool BitToBool(int bit) => bit != 0; public override string ToString() { var result = ""; for (int row = 0; row < NumberOfRows; row++) { for (int col = 0; col < NumberOfColumns; col++) { int index = row * NumberOfColumns + col; result += BoolToBit(data[index]); } result += Environment.NewLine; // dodanie znaku końca linii } return result; } public bool Equals(BitMatrix? matrix) { if (matrix == null || NumberOfRows != matrix.NumberOfRows || NumberOfColumns != matrix.NumberOfColumns) return false; for (int i = 0; i < data.Length; i++) { if (data[i] != matrix.data[i]) return false; } return true; } public override bool Equals(object? obj) { if (obj == null || GetType() != obj.GetType()) return false; else return Equals((BitMatrix)obj); } public override int GetHashCode() { int hash = 17; hash = hash * 23 + NumberOfRows.GetHashCode(); hash = hash * 23 + NumberOfColumns.GetHashCode(); for (int i = 0; i < data.Length; i++) { hash = hash * 23 + data[i].GetHashCode(); } return hash; } public static bool operator ==(BitMatrix matrix1, BitMatrix matrix2) { if (ReferenceEquals(matrix1, matrix2)) return true; if (matrix1 == null || matrix2 == null) return false; return matrix1.Equals(matrix2); } public static bool operator !=(BitMatrix matrix1, BitMatrix matrix2) { return !(matrix1 == matrix2); } }
原因分析
问题出在operator ==的实现逻辑中:
public static bool operator ==(BitMatrix matrix1, BitMatrix matrix2) { if (ReferenceEquals(matrix1, matrix2)) return true; if (matrix1 == null || matrix2 == null) return false; // 此处触发无限递归 return matrix1.Equals(matrix2); }
由于已经重载了==运算符,Mono的C#7环境中执行matrix1 == null时,会递归调用当前的op_Equality方法,导致无限循环,最终堆栈溢出。而最新.NET环境中编译器有优化,会优先使用对象默认的null检查逻辑,不会触发重载的运算符,因此没有问题。
修复方案
将operator ==及Equals(BitMatrix? matrix)方法中的== null替换为ReferenceEquals来做null检查,避免递归调用:
- 修改
operator ==方法:
public static bool operator ==(BitMatrix matrix1, BitMatrix matrix2) { if (ReferenceEquals(matrix1, matrix2)) return true; if (ReferenceEquals(matrix1, null) || ReferenceEquals(matrix2, null)) return false; return matrix1.Equals(matrix2); }
- 修改
Equals(BitMatrix? matrix)方法:
public bool Equals(BitMatrix? matrix) { if (ReferenceEquals(matrix, null) || NumberOfRows != matrix.NumberOfRows || NumberOfColumns != matrix.NumberOfColumns) return false; for (int i = 0; i < data.Length; i++) { if (data[i] != matrix.data[i]) return false; } return true; }
注:Equals(object? obj)中的obj == null可以保留,因为此处比较的是object类型,不会触发重载的==运算符。
内容的提问来源于stack exchange,提问作者AsonoJake
相关产品推荐
相关产品推荐

