【问题标题】:ArgumentException & process memory keeps increasing in C#ArgumentException 和进程内存在 C# 中不断增加
【发布时间】:2025-11-22 17:40:02
【问题描述】:

我已经编写和修改了经典蛇游戏的源代码。随着等级的增加,会生成更多的苹果(例如,等级 1 将生成 1 个苹果,等级 2 将生成 2 个苹果,依此类推。)当我到达 5 级时,生成了 5 个苹果,同时我的进程内存,从 400MB 到 2GB。那就是弹出“ArgumentException”错误并且游戏崩溃的地方。抱歉我的代码不好,因为我还在学习。

显示给我的 draw() 方法的错误,我每 500 毫秒调用一次以刷新板。

Board.cs 中的 draw() 方法发生错误

public void draw(Position p, Image pic)
    {
        squares[p.getRowNo(), p.getColNo()].Image = pic;
    }

form1.cs 中的刷新方法

private void refresh(Object myObject, EventArgs myEventArgs)
    {
        mySnake.move(mode); //Move the snake based on mode

        mainBoard.draw();
        apples.draw();  //<----- draw apples
        mySnake.draw();

        //increment the duration by amount of time that has passed
        duration += speed;
        timerLBL.Text = Convert.ToString(duration / 1000); //Show time passed


        //Check if snake is biting itself. If so, call GameOver.
        if (mySnake.checkEatItself() == true)
        {
            GameOver();
        }
        else if (apples.checkIFSnakeHeadEatApple( mySnake.getHeadPosition()) == true)
        {
            score += apples.eatAppleAtPostion(mySnake.getHeadPosition());

            scoreLBL.Text = Convert.ToString(score);


            if (apples.noMoreApples() == true)
            {
                EatenAllApple();
                clock.Stop();
                level++;
                gotoNextLevel(level);
                MessageBox.Show("Press the start button to go to Level " + level, "Congrats");
            }
            else
            {
                //Length the snake and continue with the Game
                mySnake.extendBody();
            }
        }
    }

Overal Board.cs

class Board
{
    int maxRow = 10, maxCol = 20;       //Max 10 rows, and 20 columns in the board
    int squareSize = 30;                //Each square is 30px by 30px

    PictureBox[,] squares;

    public Board(Form mainForm)
    {
        squares = new PictureBox[maxRow, maxCol];
        for (int row = 0; row < maxRow; row++)
        {
            for (int col = 0; col < maxCol; col++)
            {
                squares[row, col] = new PictureBox();
                squares[row, col].Location = new Point(col * squareSize, row * squareSize);
                squares[row, col].Height = squareSize;
                squares[row, col].Width = squareSize;
                squares[row, col].SizeMode = PictureBoxSizeMode.StretchImage;
                squares[row, col].BackColor = Color.Black;
                squares[row, col].BorderStyle = BorderStyle.FixedSingle;

                mainForm.Controls["boardPanel"].Controls.Add(squares[row, col]);
            }
        }
        mainForm.Controls["controlPanel"].Location = new Point(mainForm.Controls["boardPanel"].Location.X, mainForm.Controls["boardPanel"].Location.Y + mainForm.Controls["boardPanel"].Height + 20);
    }

    //setter
    public void setMaxColNo(int x)
    {
        maxCol = x;
    }

    public void setMaxRowNo(int x)
    {
        maxRow = x;
    }
    //getter
    public int getMaxColNo()
    {
        return maxCol-1; //Last Column No is 19, not 20
    }

    public int getMaxRowNo()
    {
        return maxRow-1; //Last Row No is 9, not 10
    }

    public int getMinColNo()
    {
        return 0;       // 0 is the smallest Col number of the board
    }

    public int getMinRowNo()
    {
        return 0;       // 0 is the smallest Row number of the board
    }

    public void draw()
    {
        for (int row = 0; row < maxRow; row++)
        {
            for (int col = 0; col < maxCol; col++)
            {
                squares[row, col].Image = null ;
            }
        }
    }

    public void draw(Position p, Image pic)
    {
        squares[p.getRowNo(), p.getColNo()].Image = pic;
    }
}

Rewards.cs(根据@AxelWass 的要求)

class Rewards
{
    List<Position> appleList;
    Board mainBoard;

    public Rewards(int size, Board mainBoard)
    {
        this.mainBoard = mainBoard;
        appleList = new List<Position>();
        for (int i=0;i< size;i++)
        {
            int rowNo, colNo;

            //Generate an apple at random position but not duplicated
            do
            {
                //Generate a random number between 1 and MaxRowNo
                rowNo = (new Random()).Next(1, mainBoard.getMaxRowNo()+1);

                //Generate a random number between 1 and MaxColNo
                colNo = (new Random()).Next(1, mainBoard.getMaxColNo()+1);

            } while (isDuplicate(rowNo, colNo) == true);

            appleList.Add(new Position(rowNo, colNo));
        }
    }

    private Boolean isDuplicate(int row, int col)
    {
        Boolean result = false;

        for (int i=0;i< appleList.Count;i++)
        {
            if (appleList[i].getRowNo() == row && appleList[i].getColNo() == col)
                result = true;
        }

        return result;
    }

    public void draw()
    {
        for (int i = 0; i < appleList.Count; i++)
        {
            mainBoard.draw(appleList[i], Properties.Resources.apple);
        }     
    }

    public Boolean checkIFSnakeHeadEatApple(Position snakeHead)
    {
        Boolean result = false;

        for (int i = 0; i < appleList.Count; i++)
        {
            if (snakeHead.getRowNo() == appleList[i].getRowNo() && snakeHead.getColNo() == appleList[i].getColNo())
                result = true;
        }
        return result;
    }

    public Boolean checkIFSnakeEatApple(Position snakeHead)
    {
        Boolean result = false;

        for (int i = 0; i < appleList.Count; i++)
        {
            if (snakeHead.getRowNo() == appleList[i].getRowNo() && snakeHead.getColNo() == appleList[i].getColNo())
                result = true;
        }
        return result;
    }

    public int eatAppleAtPostion(Position p)
    {
        for (int i = 0; i < appleList.Count; i++)
        {
            if (p.getRowNo() == appleList[i].getRowNo() && p.getColNo() == appleList[i].getColNo())
                appleList.RemoveAt(i);
            //snakeEatApple();
        }


        return 50;  //50 points per apple
    }

    public Boolean noMoreApples()
    {
        if (appleList.Count > 0)
            return false;

        else
            return true;

    }

    /*public void snakeEatApple()
    {
        System.Media.SoundPlayer EatenApple = new System.Media.SoundPlayer(Properties.Resources.Eating_Apple);
        EatenApple.Play();
    }*/


}

【问题讨论】:

  • 我认为您没有显示代码的相关部分。问题可能在 apples.draw() 内部。一种常见的设计模式是称为 Repository 的可能会对您有所帮助。您只能为所有苹果实例化一个 Image 并重用它。你能告诉我们苹果类里面的代码吗?谢谢
  • 是的,当然...我会编辑我的问题
  • Properties.Resources.apple - 这会在每次调用时反序列化图像,尽量避免这种情况。
  • 那么@Caramiriel 你有什么建议?

标签: c# visual-studio-2015 argumentexception


【解决方案1】:
   mainBoard.draw(appleList[i], Properties.Resources.apple);

这是问题陈述。 VS 中的资源设计器设计得不是很好,违反了微软自己的编码准则。一点不明显的是,apple 属性在您每次使用它时都会创建一个新的 Bitmap 对象。由于它在一个循环中,在一个本身会被非常频繁地调用的方法中,代码会生成一个很多位图对象。

Bitmap 是一个一次性的类。不处理它通常很麻烦,它是一个非常小的包装类,可以使用大量的非托管内存。如果垃圾收集器没有足够频繁地运行以使终结器可以运行,那么程序的内存使用量可能会很快耗尽。

解决方法是只使用该属性一次。将其存储在您的类的字段中(在方法之外):

  Bitmap apple = Properties.Resources.apple;

并修正语句:

  mainBoard.draw(appleList[i], apple);

如果你越过你的 T 并点你的 I,那么你在表单关闭时处理它:

  private void Form1_FormClosed(object sender, FormClosedEventArgs e) {
      apple.Dispose();
  }

这是一个好习惯,尽管它可能是不必要的,因为您的程序可能会在用户关闭窗口时结束。

【讨论】:

  • 你是对的:“在内部,get 属性使用 ResourceManager 类来创建对象的新实例”。来自documentation