C#编辑_CompleteMap时同步修改CurrentMap.MapData的问题求解
提问内容
开发运行在Windows控制台的娱乐小游戏时,DrawGame()方法出现异常。
- 定义字符串数组
string[] _CompleteMap,直接从同为字符串数组的CurrentMap.MapData赋值:string[] _CompleteMap = CurrentMap.MapData; - 原本计划将
CurrentMap.MapData作为空白底图,通过修改数组内字符串的字符标记玩家、怪物位置,但修改_CompleteMap时,CurrentMap.MapData的内容会同步变化,导致玩家走过的路径留下永久痕迹。
附现有代码:
using System; using System.Text; using System.Threading; using ConsoleAdventure.Engine.BaseClasses; using Database = ConsoleAdventure.Engine.Database; namespace ConsoleAdventure { class Program { private static bool whileGameScene = true; private static Engine.BaseClasses.MapBase CurrentMap; static unsafe void Main(string[] args) { // Load and populate the databases. Database.ItemDB.LoadItemDB(); Database.MapDB.LoadMapDB(); // Set demo map CurrentMap = Database.MapDB.Maps[0]; // Hide cursor Console.CursorVisible = false; // Start a new thread to capture the user inputs new Thread(() => { while (true) { Thread.CurrentThread.IsBackground = true; ConsoleKeyInfo cki = Console.ReadKey(); if (cki.Key == ConsoleKey.A && Player.Data.Location.X > 0) Player.Data.Location.X--; else if (cki.Key == ConsoleKey.D) Player.Data.Location.X++; else if (cki.Key == ConsoleKey.W && Player.Data.Location.Y > 0) Player.Data.Location.Y--; else if (cki.Key == ConsoleKey.S) Player.Data.Location.Y++; CheckWarpPoints(); } }).Start(); // Draw the game while (whileGameScene) { DrawGame(); } } /// <summary> /// Check if player has stood on a warp zone /// </summary> private static void CheckWarpPoints() { foreach (MapBase.WarpPoint warp in Database.MapDB.Maps[Player.Data.CurrentMap].WarpPoints) { if (Player.Data.Location.X == warp.From.X && Player.Data.Location.Y == warp.From.Y) { Player.Data.CurrentMap = warp.ToMapIndex; Player.Data.Location = warp.To; } } } public static void DrawGame() { string[] _CompleteMap = CurrentMap.MapData; StringBuilder _sbRow = new StringBuilder(); foreach (var _object in CurrentMap.MapObjects) { _sbRow = new StringBuilder(_CompleteMap[_object.CurrentLocation.Y]); _sbRow[_object.CurrentLocation.X] = _object.Icon; _CompleteMap[_object.CurrentLocation.Y] = _sbRow.ToString(); } _sbRow = new StringBuilder(_CompleteMap[Player.Data.Location.Y]); _sbRow[Player.Data.Location.X] = 'X'; _CompleteMap[Player.Data.Location.Y] = _sbRow.ToString(); for (int i = 0; i < _CompleteMap.Length; i++) { Console.SetCursorPosition(0, i); Console.WriteLine(_CompleteMap[i]); } } } }
问题解答
核心原因
C#中数组属于引用类型,直接执行string[] _CompleteMap = CurrentMap.MapData时,不会复制数组的实际内容,仅让两个变量指向内存中同一个数组实例。对_CompleteMap的所有元素修改,本质上都是直接修改原CurrentMap.MapData指向的数组内容,导致空白底图被持续污染。
修复方案
创建_CompleteMap时,对原数组做完整复制,而非直接赋值:
// 写法1:使用Array.Clone,无需引入额外命名空间 string[] _CompleteMap = (string[])CurrentMap.MapData.Clone(); // 写法2:引入System.Linq后使用ToArray扩展方法 // string[] _CompleteMap = CurrentMap.MapData.ToArray();
由于数组内元素是不可变的string类型,上述浅复制即可满足需求:修改副本数组的元素时,只会替换副本内的字符串引用,不会影响原数组的底图内容。
额外代码优化
- 输入线程中
Thread.CurrentThread.IsBackground = true写在循环内无效,应在线程启动前设置:
var inputThread = new Thread(() => { /* 原有输入逻辑 */ }); inputThread.IsBackground = true; inputThread.Start();
- 玩家移动逻辑缺少右、下边界校验,按D/S键超出地图范围时会触发数组越界异常,补充对应边界判断即可。
- Main方法标记的
unsafe关键字当前无对应不安全代码逻辑,可直接移除,避免项目开启不安全编译带来的不必要风险。 - 当前绘制逻辑每次全量输出整个地图,控制台开销较大,后续可优化为仅重绘玩家位置变化的前后两行内容。
内容的提问来源于stack exchange,提问作者Paul Baron Gikkingen
相关产品推荐
相关产品推荐

