You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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检查,避免递归调用:

  1. 修改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);
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 07:52:54