正确处理位图对象

Properly disposing of Bitmap object

我正在使用 C# Winforms 面板绘制图像:

private void DrawPanel_Paint(object sender, PaintEventArgs e)
{
    DrawOnPanel(e.Graphics);
}

被调用的方法从我的资源 (myImage) 获取现有图像,将其提供给另一个调整图像大小的方法,returns 调整大小的图像以便绘制。

public static void DrawOnPanel(Graphics g)
{
    var _resizedImage = ResizeImage(Resources.myImage);
    g.DrawImage(_resizedImage, destX, destY);

    // ... several other images resized & manipulated then drawn

}

图片调整功能为:

public Bitmap ResizeImage(Bitmap image)
{
    int scale = 3;
    var destImage= new Bitmap(image.Width * scale, image.Height * scale);
    destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
    using (var graphics = Graphics.FromImage(destImage))
    {
    graphics.CompositingMode = CompositingMode.SourceCopy;
    graphics.CompositingQuality = CompositingQuality.HighSpeed;
    graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
    graphics.SmoothingMode = SmoothingMode.HighSpeed;
    graphics.PixelOffsetMode = PixelOffsetMode.None;

    using var wrapMode = new ImageAttributes();
    wrapMode.SetWrapMode(WrapMode.TileFlipXY);
    graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
    }
    return destImage;
}

程序在其循环中不断调用 DrawPanel.Invalidate()。 每次调用 DrawPanel.Invalidate() 时,我都会检测到内存泄漏。内存消耗一直在稳步上升,直到 GC 处理它为止。虽然这不是破坏游戏的问题,但我仍然想知道我应该在哪里以及如何处理上面代码中的对象。 我尝试在上面的 DrawOnPanel 方法中使用 using var _resizedImage = ResizeImage(Resources.myImage); 但程序 returns 出现错误 System.ArgumentException: 'Parameter is not valid.'。如果我删除 using 没有错误。

每次调用 ResizeImage 时,您都会创建一个新的位图。一旦您知道不再需要该位图,就应立即将其处置。在图形上绘制后,您似乎不需要调整大小的图像。因此,我建议进行以下更改:

public static void DrawOnPanel(Graphics g)
{
    using (Image_resizedImage = ResizeImage(Resources.myImage))
    {
        g.DrawImage(_resizedImage, destX, destY);
    }
    // resizedImage is Disposed

    // ... several other images resized & manipulated then drawn
}

改进空间

您当前的设计将在每次调用 DrawOnPanel 时创建一个新的调整大小的位图。在我看来,大多数时候 Resources.myImage 调整大小后调整大小的位图将是相同的。你为什么不记住它,直到影响调整大小的位图的参数发生变化?

如果我查看代码,您似乎总是从原始图像创建相同大小的图像。因此,您甚至可以将所有调整大小的图像放入一个字典中以便快速查找:

// get all Image resources that you will resize and put them in a Dictionary
// Key original Bitmap, Value: resized Bitmap
private Dictionary<Bitmap, BitMap> images = this.FetchImages()
    .ToDictionary(
       // Parameter keySelector: key is original image
       image => image,
       // Parameter valueSelector: value is the resized image
       imgage => ResizeImage(original));

显示调整大小后的图像现在会快得多:只需查找。

public static void DrawOnPanel(Graphics g)
{
    var _resizedImage = this.Images[Resources.myImage];
    g.DrawImage(_resizedImage, destX, destY);
   // ... several other images resized & manipulated then drawn
}

不要忘记在关闭表单时或最迟在处理表单时处理位图:

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        foreach (var resizedImage in images.Values)
            resizedImage.Dispose();
    }
}