You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

重构8080太空侵略者模拟器时新建类引发Stack Overflow错误求助

解决栈溢出问题:循环引用导致的无限实例化

你遇到的System.StackOverflowException是循环引用引发的无限递归实例化问题,咱们一步步拆解原因和解决方案:

问题根源

看你三个类的实例化逻辑,形成了一个无限循环的创建链:

  1. _8080类内部直接实例化了CPU和MainForm
  2. CPU类内部又实例化了_8080
  3. MainForm的构造函数里还实例化了_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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 22:47:26