重构8080太空侵略者模拟器时新建类引发Stack Overflow错误求助
解决栈溢出问题:循环引用导致的无限实例化
你遇到的System.StackOverflowException是循环引用引发的无限递归实例化问题,咱们一步步拆解原因和解决方案:
问题根源
看你三个类的实例化逻辑,形成了一个无限循环的创建链:
_8080类内部直接实例化了CPU和MainFormCPU类内部又实例化了_8080MainForm的构造函数里还实例化了_8080
这就导致:创建MainForm时会生成_8080,生成_8080时会生成CPU,生成CPU时又会生成_8080……这个循环会不断消耗栈内存,最终触发栈溢出。
解决方案:打破循环,手动传递实例
核心思路是移除类内部自动创建其他类对象的逻辑,改为通过外部传递已创建好的实例,斩断循环链。
步骤1:修改类的构造函数,移除内部实例化
修改_8080类
namespace SpaceInvaders { class _8080 { private CPU _cpu; private MainForm _mainForm; // 通过构造函数接收外部传递的CPU和MainForm实例 public _8080(CPU cpu, MainForm mainForm) { _cpu = cpu; _mainForm = mainForm; } public void StartEmulator() { _cpu.initialiseEmulator(); // Reset LoadProgram("invaders.rom"); // Load ROM _cpu.emulateCPU(); } private bool LoadProgram(string filename) { Console.WriteLine("\nLoading: " + filename + "\n"); byte[] loadedProgramBytes = null; try { loadedProgramBytes = File.ReadAllBytes(filename); } catch (Exception Ex) { Debug.WriteLine(Ex.ToString()); } if (loadedProgramBytes == null) { Debug.WriteLine("Error loading program. The byte array loaded is null."); return false; } int lSize = loadedProgramBytes.Count(); for (int i = 0; i < lSize; ++i) { _cpu.memory[i] = loadedProgramBytes[i]; } Debug.WriteLine("ROM loaded in successfully.\n"); return true; } private void updateScreen(int addr, int val) { int x = addr >> 5; int y = 255 - ((addr & 0x1f) << 3); for (int bit = 1; bit <= 128; bit <<= 1) { //_mainForm.screenBuffer.SetPixel(x, y--, (bit & val) != 0 ? Color.White : Color.Black); } //Bitmap clone = (Bitmap)_mainForm.screenBuffer.Clone(); //_mainForm.pictureBox1.BackgroundImage = clone; } } }
修改CPU类
删掉内部的_8080实例化,改为通过构造函数接收:
namespace SpaceInvaders { class CPU { private _8080 _emulator; public byte[] memory = new byte[65536]; // 其他成员变量保持不变 // 构造函数接收外部传递的_8080实例 public CPU(_8080 emulator) { _emulator = emulator; } public void initialiseEmulator() { // 原有初始化逻辑保持不变 } public void emulateCPU() { // 原有模拟逻辑保持不变,调用_8080的方法时使用传递进来的实例 } } }
修改MainForm类
调整实例化顺序,先创建空引用,再逐步绑定:
namespace SpaceInvaders { public partial class MainForm : Form { private _8080 _emulator; private CPU _cpu; public Bitmap screenBuffer; public MainForm() { InitializeComponent(); screenBuffer = new Bitmap(256, 224); // 先创建临时空引用,再依次创建实例绑定 _emulator = null; _cpu = new CPU(_emulator); _emulator = new _8080(_cpu, this); } // 示例:按钮点击启动模拟器 private void btnStart_Click(object sender, EventArgs e) { _emulator.StartEmulator(); } } }
步骤2:验证逻辑
修改后实例化顺序变成:MainForm → CPU(传入临时空的_emulator)→ _8080(传入已创建的CPU和MainForm)→ 完成循环绑定
如果CPU初始化时不需要立即使用_8080,这个逻辑完全可行;如果需要,可以后续通过属性手动赋值,进一步优化。
额外编码建议
- 类名遵循C#规范:把
_8080改成SpaceInvaders8080或Emulator8080,下划线通常用于私有字段,不适合类名 - 私有字段统一用下划线前缀:比如
_cpu、_mainForm,和公共成员清晰区分 - 后续如果依赖关系更复杂,可以考虑使用依赖注入容器(比如Microsoft.Extensions.DependencyInjection),现在手动传递实例足够解决问题
内容的提问来源于stack exchange,提问作者Jay Benton
相关产品推荐
相关产品推荐

