c# - 为什么我会收到错误 "NullReferenceException was unhandled"?

标签 c# compiler-errors null nullpointerexception nullreferenceexception

我正在尝试制作 slider 益智游戏,但在我的 form1 中调用 myBoard.paint(e.Graphics) 时,我不断收到错误“NullReferenceException 未处理”。请帮助我!!!

这是我的 Form1 代码(如果我需要发布一些其他类代码,请告诉我):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;

namespace SliderPuzzle
{
    public partial class Form1 : Form
    {
        private int tileSize;
        private int rowsCols;
        private SlidePuzzle myBoard;
        private Stopwatch timer;
        private int moveCount;

        public Form1()
        {
            InitializeComponent();
            pictureBox1.TabIndex = 3;
            pictureBox1.Size = new Size(100, 50);
            pictureBox1.Location = new Point(16, 71);
            pictureBox1.BackColor = Color.PaleGreen;
            pictureBox1.BorderStyle = BorderStyle.Fixed3D;
            pictureBox1.TabStop = false;
            tileSize = imageList1.ImageSize.Width;
            rowsCols = 3;
            pictureBox1.Width = rowsCols * tileSize;
            pictureBox1.Height = rowsCols * tileSize;
        }

        public void initGame()
        {
            myBoard = new SlidePuzzle(rowsCols, tileSize, imageList1);
            timer = new Stopwatch();
            moveCount = 0;
            timer.Start();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            initGame();
        }

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
          this.myBoard.paint(e.Graphics);
        }

        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if (myBoard.move(e.Y / tileSize, e.X / tileSize))
                ++moveCount;
            Refresh();
            if (!myBoard.winner())
                return;
            timer.Stop();
            if (MessageBox.Show(string.Format("You won!!\nIt took you {0} moves and {1:F2} seconds.\nPlay again?", (object)moveCount, (object)timer.Elapsed.TotalSeconds), "Game Over", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == DialogResult.No)
            {
                Close();
            }
            else
            {
                initGame();
                Refresh();
            }
        }
    }
}

更新 #1:好的,所以我移动了 myBoard = new SlidePuzzle(rowsCols, tileSize, imageList1);给我的构造函数,但现在没有任何图像显示在上面。这是它的外观与它应该看起来的样子:enter image description here

编辑#2:好的,我把它移回原来的位置并放上

if (this.myBoard != null)
        this.myBoard.paint(e.Graphics);

相反,它工作得更好,看起来也更好。但是图像不显示仍然是个问题。

编辑 #3:这是 SliderPuzzle.Paint 代码:

        public void paint(Graphics g)
    {
        for (int r = 0; r < this.myGrid.getNumRows(); ++r)
        {
            for (int c = 0; c < this.myGrid.getNumCols(); ++c)
                this.myGrid.get(new Location(r, c)).paint(g);
        }
    }

编辑 #4:这是 SliderPuzzle 类的代码:

using System;
using System.Drawing;
using System.Windows.Forms;

namespace SliderPuzzle
{
    internal class SlidePuzzle
    {
        private static Random rand = new Random();
        private int myTileSize;
        private BoundedGrid myGrid;
        private ImageList myImages;
        private Location myBlankLoc;

        static SlidePuzzle()
        {
        }

        public SlidePuzzle(int rowsCols, int tileSize, ImageList images)
        {
            this.myTileSize = tileSize;
            this.myGrid = new BoundedGrid(rowsCols, rowsCols);
            this.myImages = images;
            this.myBlankLoc = new Location(rowsCols - 1, rowsCols - 1);
            this.initBoard();
        }

        private void initBoard()
        {
            int index1 = 0;
            for (int r = 0; r < this.myGrid.getNumRows(); ++r)
            {
                for (int c = 0; c < this.myGrid.getNumCols(); ++c)
                {
                    this.myGrid.put(new Location(r, c), new Tile(index1, this.myTileSize, new Location(r, c), this.myImages.Images[index1]));
                    ++index1;
                }
            }
            for (int index2 = 0; index2 < 1000; ++index2)
            {
                Location adjacentLocation = this.myBlankLoc.getAdjacentLocation(SlidePuzzle.rand.Next(4) * 90);
                if (this.myGrid.isValid(adjacentLocation))
                {
                    this.swap(this.myBlankLoc, adjacentLocation);
                    this.myBlankLoc = adjacentLocation;
                }
            }
        }

        public bool move(int row, int col)
        {
            Location loc1 = new Location(row, col);
            if (Math.Abs(this.myBlankLoc.getRow() - row) + Math.Abs(this.myBlankLoc.getCol() - col) != 1)
                return false;
            this.swap(loc1, this.myBlankLoc);
            this.myBlankLoc = loc1;
            return true;
        }

        public bool winner()
        {
            int num = 0;
            for (int r = 0; r < this.myGrid.getNumRows(); ++r)
            {
                for (int c = 0; c < this.myGrid.getNumCols(); ++c)
                {
                    if (this.myGrid.get(new Location(r, c)).getValue() != num)
                        return false;
                    ++num;
                }
            }
            return true;
        }

        private void swap(Location loc1, Location loc2)
        {
            Tile tile1 = this.myGrid.put(loc2, this.myGrid.get(loc1));
            Tile tile2 = this.myGrid.put(loc1, tile1);
            tile1.setLocation(loc1);
            tile2.setLocation(loc2);
        }

        public void paint(Graphics g)
        {
            for (int r = 0; r < this.myGrid.getNumRows(); ++r)
            {
                for (int c = 0; c < this.myGrid.getNumCols(); ++c)
                    this.myGrid.get(new Location(r, c)).paint(g);
            }
        }
    }
}

更新 #5:这是 Tile 类:

using System.Drawing;

namespace SliderPuzzle
{
    internal class Tile
    {
        private int myValue;
        private int mySize;
        private Location myLoc;
        private Image myImage;

        public Tile(int value, int tileSize, Location loc, Image img)
        {
            this.myValue = value;
            this.mySize = tileSize;
            this.myLoc = loc;
            this.myImage = img;
        }

        public int getValue()
        {
            return this.myValue;
        }

        public void setLocation(Location newLoc)
        {
            this.myLoc = newLoc;
        }

        public void paint(Graphics g)
        {
            g.DrawImage(this.myImage, this.myLoc.getCol() * this.mySize, this.myLoc.getRow() * this.mySize);
        }
    }
}

编辑 #6:这是位置类:

namespace SliderPuzzle
{
    internal class Location
    {
        public const int LEFT = -90;
        public const int RIGHT = 90;
        public const int HALF_LEFT = -45;
        public const int HALF_RIGHT = 45;
        public const int FULL_CIRCLE = 360;
        public const int HALF_CIRCLE = 180;
        public const int AHEAD = 0;
        public const int NORTH = 0;
        public const int NORTHEAST = 45;
        public const int EAST = 90;
        public const int SOUTHEAST = 135;
        public const int SOUTH = 180;
        public const int SOUTHWEST = 225;
        public const int WEST = 270;
        public const int NORTHWEST = 315;
        private int row;
        private int col;

        public Location(int r, int c)
        {
            this.row = r;
            this.col = c;
        }

        public int getRow()
        {
            return this.row;
        }

        public int getCol()
        {
            return this.col;
        }

        public Location getAdjacentLocation(int direction)
        {
            int num1 = (direction + 22) % 360;
            if (num1 < 0)
                num1 += 360;
            int num2 = num1 / 45 * 45;
            int num3 = 0;
            int num4 = 0;
            if (num2 == 90)
                num3 = 1;
            else if (num2 == 135)
            {
                num3 = 1;
                num4 = 1;
            }
            else if (num2 == 180)
                num4 = 1;
            else if (num2 == 225)
            {
                num3 = -1;
                num4 = 1;
            }
            else if (num2 == 270)
                num3 = -1;
            else if (num2 == 315)
            {
                num3 = -1;
                num4 = -1;
            }
            else if (num2 == 0)
                num4 = -1;
            else if (num2 == 45)
            {
                num3 = 1;
                num4 = -1;
            }
            return new Location(this.getRow() + num4, this.getCol() + num3);
        }

        public bool equals(Location other)
        {
            if (this.getRow() == other.getRow())
                return this.getCol() == other.getCol();
            else
                return false;
        }

        public int hashCode()
        {
            return this.getRow() * 3737 + this.getCol();
        }

        public int compareTo(Location otherLoc)
        {
            if (this.getRow() < otherLoc.getRow())
                return -1;
            if (this.getRow() > otherLoc.getRow())
                return 1;
            if (this.getCol() < otherLoc.getCol())
                return -1;
            return this.getCol() > otherLoc.getCol() ? 1 : 0;
        }

        public string toString()
        {
            return "(" + (object)this.getRow() + ", " + (string)(object)this.getCol() + ")";
        }
    }
}

编辑 #7:这是最后一个类,BoundedGrid 类:

using System;
using System.Collections.Generic;

namespace SliderPuzzle
{
    internal class BoundedGrid
    {
        private Tile[,] occupantArray;

        public BoundedGrid(int rows, int cols)
        {
            this.occupantArray = new Tile[rows, cols];
        }

        public int getNumRows()
        {
            return this.occupantArray.GetLength(0);
        }

        public int getNumCols()
        {
            return this.occupantArray.GetLength(1);
        }

        public bool isValid(Location loc)
        {
            if (0 <= loc.getRow() && loc.getRow() < this.getNumRows() && 0 <= loc.getCol())
                return loc.getCol() < this.getNumCols();
            else
                return false;
        }

        public List<Location> getOccupiedLocations()
        {
            List<Location> list = new List<Location>();
            for (int r = 0; r < this.getNumRows(); ++r)
            {
                for (int c = 0; c < this.getNumCols(); ++c)
                {
                    Location loc = new Location(r, c);
                    if (this.get(loc) != null)
                        list.Add(loc);
                }
            }
            return list;
        }

        public Tile get(Location loc)
        {
            if (!this.isValid(loc))
                throw new Exception("Location " + (object)loc + " is not valid");
            else
                return this.occupantArray[loc.getRow(), loc.getCol()];
        }

        public Tile put(Location loc, Tile obj)
        {
            if (!this.isValid(loc))
                throw new Exception("Location " + (object)loc + " is not valid");
            if (obj == null)
                throw new NullReferenceException("obj == null");
            Tile tile = this.get(loc);
            this.occupantArray[loc.getRow(), loc.getCol()] = obj;
            return tile;
        }

        public Tile remove(Location loc)
        {
            if (!this.isValid(loc))
                throw new Exception("Location " + (object)loc + " is not valid");
            Tile tile = this.get(loc);
            this.occupantArray[loc.getRow(), loc.getCol()] = (Tile)null;
            return tile;
        }
    }
}

编辑 #8:当我点击图片框时,程序崩溃并提示 timer.Stop();在 form1 中给我一个 NullReferenceException!!!

编辑 #9:好的,这很有效...我发现图像仍然没有显示,但我认为它们从未被放置在网格上。当我点击网格时(仍然没有图像)它说我赢了。这应该只在我将图 block 移动到正确的顺序后才会显示。知道发生了什么事吗?

编辑 #10:我的程序现在终于可以运行了!原来我在 form1 的构造函数中放错了地方,现在一切正常了!图像显示一切!多酷啊!!!

感谢大家的贡献,现在我的学校项目将取得好成绩!

最佳答案

这是让您头疼的问题之一。当您可以保证事件永远不会被乱序调用时,尝试确定事件的确切顺序是可以的。不幸的是,Paint 事件会在各种奇怪的时间触发,甚至可能在 Load 事件之前触发。

唯一真正的答案是从不依赖以特定顺序触发的事件。在尝试绘制之前,请始终检查您的 myBoard 对象是否有效:

private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
    if (this.myBoard != null)
        this.myBoard.paint(e.Graphics);
}

是的,正如其他人指出的那样,最好尽快创建所有对象 - 例如在构造函数中,这毕竟是构造函数的用途。即使那样,您仍然可以通过构造函数中的操作触发事件,这会毁了您一整天。您的偶数处理程序应该在构造函数代码中设置得尽可能晚,以解决这个问题。

经验法则:

  • 如果您正在创建将在事件处理程序中使用的自定义对象,请始终尝试在构造函数中调用 InitializeComponent 之前创建并初始化它们。

  • 尽可能晚地连接你的事件处理程序,尤其是像 Paint 这样的事情 - 在你的构造函数完成之前它不会有用,所以在构造函数中最后做。

关于c# - 为什么我会收到错误 "NullReferenceException was unhandled"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18752435/

相关文章:

error-handling - SMLcircularity操作数和运算符的错误不一致

c# - 什么是NullReferenceException,如何解决?

c# - ASP.NET 网站项目构建但发布时出错

c# - 如何将 SOAP header 附加到我的 C# 客户端?

model-view-controller - MVC Lazarus 参数数量错误

java - Jython : cannot import name String [from java.util] [closed]

c# - 在c#中将文件写入用户桌面

c# - 字典 ArgumentException 日志重复键 : which is more performant?

java - 二维数组中的第一个元素变为空?

java - 为什么不调用从返回 null 的静态方法链接的静态变量抛出 NPE?