实例化包含位图的自定义获取的结构时出现 StackOverflow

问题描述 投票:0回答:1

我正在尝试向我正在编写的程序添加一些图形,为此我想组合两个位图。我想避免显式地组合它们,因此我编写了一些代码来在每次调用时处理组合。

public struct Map
{
    ...
    public Bitmap Display 
    { 
        get 
        {
            using (Graphics g = Graphics.FromImage(this.Display))
            {
                g.DrawImage(this.Background, new Rectangle(0, 0, Display_Width, Display_Height));
                g.DrawImage(this.Room_Grid, new Rectangle(0, 0, Display_Width, Display_Height));

                g.Dispose();
            }

            return this.Display;
        } 
        private set { } 
    }
    ...
}

我之前曾使用类似的代码取得过成功,但这次它在编译和实例化结构后抛出了 StackOverflow。

为什么会发生这种情况以及如何在保持功能的同时解决问题。

编辑:

到目前为止,谢谢大家。

我现在明白了这个问题。我想修复它,而无需创建内存不足的传输位图(不知道为什么,收集器仅每 10 到 20 秒运行一次),并且我想避免使用方法,因为它始终是组合静态背景与变化前景的结合。我需要每次在显示更新图像之前添加方法调用,如果我在请求图像时可以这样做,这看起来很笨拙。

c# graphics instantiation
1个回答
0
投票

这是

Display
属性的 getter 中的第一行实际代码:

using (Graphics g = Graphics.FromImage(this.Display))

这是一个显式的无限递归,100% 的时间都会耗尽你的堆栈。从属性自己的 getter 中读取属性的值不仅是不好的做法,而且这样做几乎总是错误的。如此之多以至于它应该会导致编译器错误。

我假设您不打算这样做,有一个

display
_display
或其他一些包含实际位图的字段,并且您输入错误。为了完整起见,这就是它应该的样子(如果我正确地理解了你的意图):

private Bitmap? _display = null;

public Bitmap Display
{
    get
    {
        if (_display is not null)
            return _display;
        // Create the bitmap with whatever properties you need here
        _display = new(Display_Width, Display_Height);

        // now initialize it
        using Graphics g = Graphics.FromImage(this.Display);
        g.DrawImage(this.Background, new Rectangle(0, 0, Display_Width, Display_Height));
        g.DrawImage(this.Room_Grid, new Rectangle(0, 0, Display_Width, Display_Height));

        return _display;
    }
}

如果各种值永远不会改变,那就太好了,但我会冒险假设至少

Background
是稍微动态的。如果是这样,只要相关值发生变化,您就需要处理(和 null)
_display

例如,假设您正在为一款游戏合成背景位图,只要您从一个房间移动到另一个房间,背景就会更新。将场景中的所有静态数据缓存到仅在房间更改时更新的位图中要快一些。虽然有很多方法可以实现这一点,但理想的情况可能是重复使用相同的位图实例,只要房间的背景(或屏幕尺寸)不改变。

所以...让我们通过删除现有的

_display
位图来响应这些更改,并在下次需要时重建它。

private Bitmap? _background = null;
public Bitmap? Background
{
    get => _background;
    set
    {
        if (!ReferenceEquals(_backgroud, value))
        {
            Interlocked.Exchange(ref _display, bmp)?.Dispose();
            _background = value;
        }
    }
}

// Use whatever your default width is:
private int _displayWidth = 1920;
public int DisplayWidth 
{
    get => _displayWidth;
    set
    {
        if (_displayWidth != value)
        {
            Interlocked.Exchange(ref _display, bmp)?.Dispose();
            _displayWidth = value;
        }
    }
}

// Same for default height.
private int _displayHeight = 1080;
public int DisplayHeight
{
    get => _displayHeight;
    set
    {
        if (_displayHeight != value)
        {
            Interlocked.Exchange(ref _display, bmp)?.Dispose();
            _displayHeight = value;
        }
    }
}

// And finally, the cached display bitmap
private Bitmap? _display = null;
public Bitmap Display
{
    get
    {
        if (_display is not null)
            return _display;

        // Create new bitmap.
        _display = new(DisplayWidth, DisplayHeight);
        
        // Get the image bounds
        GraphicsUnit units = GraphicsUnit.Point;
        RectangleF bounds = _display.GetBounds(ref units);
        
        // Or just do this:
        // Rectangle bounds = new(0, 0, DisplayWidth, DisplayHeight);

        // Draw the contents of the new bitmap
        using Graphics g = Graphics.FromImage(_display);
        g.DrawImage(Background, bounds);
        g.DrawImage(Room_Grid, bounds);

        return _display;
    }
}

注意:在代码中撒满

this.
会分散注意力。当与局部变量或参数名称发生冲突时使用它,否则它只是毫无意义的噪音。更好的是,使用命名约定来确保您永远不必处理这种性质的名称冲突,并且您永远不需要再次使用
this.

© www.soinside.com 2019 - 2024. All rights reserved.