LinkedList.First.Value返回null但链表非空的异常问题求助
问题:LinkedList.First.Value返回NullReferenceException的解决办法
问题描述
开发控制台蛇游戏时,调用LinkedList.First.Value抛出NullReferenceException,提示LinkedList.First.get返回null。但已给链表首个元素设置了55,确认链表非空。
相关代码片段
链表定义
LinkedList<int> Pos = new LinkedList<int>();
Main方法代码
var p = new Program(); p.Pos.AddLast(55); Timer myTimer = new Timer(); myTimer.Elapsed += new ElapsedEventHandler(Move); myTimer.Interval = 1000; // 1000毫秒为1秒 myTimer.Start();
引发错误的Move方法代码
p.Pos.AddFirst(p.Pos.First.Value + p.dir);
异常信息
System.NullReferenceException: 'Object reference not set to an instance of an object.'
System.Collections.Generic.LinkedList.First.get returned null.
完整代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Timers; using System.Collections; namespace Snake { class Program { LinkedList<int> Pos = new LinkedList<int>(); int dir = 1; bool snakeAlive = true; static void Main(string[] args) { var p = new Program(); p.Pos.AddLast(55); Timer myTimer = new Timer(); myTimer.Elapsed += new ElapsedEventHandler(Move); myTimer.Interval = 1000; // 1000 ms is one second myTimer.Start(); do { switch (Console.ReadKey().Key) { case ConsoleKey.UpArrow: p.dir = -10; break; case ConsoleKey.DownArrow: p.dir = 10; break; case ConsoleKey.LeftArrow: p.dir = -1; break; case ConsoleKey.RightArrow: p.dir = 1; break; } } while (p.snakeAlive == true); } void drawGame() { Console.Clear(); for (int i = 0; i < 100; i++) { if (i % 10 == 9) { Console.WriteLine(IsSnake(Pos, i)); } else { Console.Write(IsSnake(Pos, i)); } } } String IsSnake(LinkedList<int> sPos, int position) { for (int i = 0; i < sPos.Count; i++) { if (position == sPos.ElementAt(i)) { return "=="; } } return "[]"; } public static void Move(object source, ElapsedEventArgs e) { var p = new Program(); p.Pos.AddFirst(p.Pos.First.Value + p.dir); p.Pos.RemoveLast(); p.drawGame(); } } }
问题原因与解决方案
原因分析
错误核心在Move方法:
public static void Move(object source, ElapsedEventArgs e) { var p = new Program(); // 新建了一个全新的Program实例 p.Pos.AddFirst(p.Pos.First.Value + p.dir); // ... }
Main方法中创建的p是已初始化Pos的游戏实例,但Move方法里重新创建了一个Program实例,这个新实例的Pos链表是空的,调用First.Value必然抛出空引用异常。
解决方案
传递正确的实例到Move方法
修改Timer事件注册逻辑,用lambda捕获Main中的p实例,同时将Move改为非静态方法:// Main方法中修改事件注册 myTimer.Elapsed += (source, e) => p.Move(source, e); // 修改Move方法为非静态 public void Move(object source, ElapsedEventArgs e) { Pos.AddFirst(Pos.First.Value + dir); Pos.RemoveLast(); drawGame(); }这样
Move操作的就是Main中已初始化的游戏实例,不会再出现空链表问题。额外优化建议
IsSnake方法用ElementAt遍历LinkedList效率极低,LinkedList不支持索引访问,改为遍历节点:String IsSnake(LinkedList<int> sPos, int position) { foreach (var pos in sPos) { if (pos == position) { return "=="; } } return "[]"; }- 按键读取时添加
Console.ReadKey(true),避免按键字符显示在控制台,提升游戏体验。
内容的提问来源于stack exchange,提问作者Leo Barber
相关产品推荐
相关产品推荐

