实例化含Bitmap自定义get的Struct时触发StackOverflow的原因与解决
解决Bitmap合并导致的StackOverflow异常(满足无中转Bitmap、无显式方法要求)
你的代码触发StackOverflow的核心原因是Display属性的getter无限递归:在Graphics.FromImage(this.Display)和return this.Display中,都在调用Display的getter,导致方法不断调用自身,直到栈空间耗尽。
我们通过缓存合并结果+自动失效机制来解决,既不用每次显式调用方法,也不会频繁创建新Bitmap浪费内存,同时保留原有属性调用的便捷性。
具体实现代码
public struct Map : IDisposable { private Bitmap _background; private Bitmap _roomGrid; private Bitmap _displayCache; private bool _displayNeedsUpdate; public Bitmap Background { get => _background; set { _background?.Dispose(); _background = value; InvalidateDisplayCache(); } } public Bitmap Room_Grid { get => _roomGrid; set { _roomGrid?.Dispose(); _roomGrid = value; InvalidateDisplayCache(); } } public Bitmap Display { get { if (_displayNeedsUpdate || _displayCache == null) { // 校验依赖资源是否存在,避免空引用 if (Background == null || Room_Grid == null) return null; // 仅在需要时创建/重建缓存Bitmap _displayCache?.Dispose(); _displayCache = new Bitmap(Background.Width, Background.Height); using (Graphics g = Graphics.FromImage(_displayCache)) { g.DrawImage(Background, new Rectangle(0, 0, Background.Width, Background.Height)); g.DrawImage(Room_Grid, new Rectangle(0, 0, Background.Width, Background.Height)); } _displayNeedsUpdate = false; } return _displayCache; } private set { } } private void InvalidateDisplayCache() { _displayNeedsUpdate = true; _displayCache?.Dispose(); _displayCache = null; } // 实现IDisposable清理Bitmap资源,避免内存泄漏 public void Dispose() { _background?.Dispose(); _roomGrid?.Dispose(); _displayCache?.Dispose(); } }
方案说明
- 解决递归问题:Display getter不再调用自身,而是操作私有缓存字段
_displayCache,彻底终止无限递归。 - 无中转Bitmap:仅在Background/Room_Grid变化时才重建缓存,平时复用同一个Bitmap,避免频繁创建销毁的内存开销。
- 无需显式调用方法:每次访问Display时自动检查缓存是否有效,自动执行合并逻辑,外部调用方式和之前完全一致。
- 资源安全:实现IDisposable接口,手动管理Bitmap资源,避免值类型持有引用资源导致的内存泄漏;同时在属性setter中及时释放旧资源。
注意事项
- 由于struct是值类型,使用完Map实例后务必调用
Dispose()方法清理资源。 - 如果Background或Room_Grid的尺寸可能变化,当前逻辑会自动重建缓存适配新尺寸,无需额外处理。
- 可根据实际需求添加空值校验的 fallback 逻辑(比如返回空白Bitmap而非null)。
内容的提问来源于stack exchange,提问作者Desdar
相关产品推荐
相关产品推荐

