我应该为每个Paint请求创建新的Pens / Brushes还是在整个应用程序生命周期中保留它们?

我有一个应用程序做了很多绘图,让我们假装它是一个类似Viso的应用程序。 它有一些对象,它们有多个绘制的子对象,可以连接的东西,resize等等。目前,当我在特定的子对象或对象上调用paint时,我会执行以下操作:

using(var pen = new Pen(this.ForeColor)) { // Paint for this object. } 

我已经阅读了相互矛盾的答案,这应该为一个不断绘制相同内容的应用程序(可能只是resize,移动等)来完成。 我应该将Pen/Brush与对象一起存储,然后在处理应用程序时将它们全部丢弃,或者它们是否足够高效以便为每次绘制调用创建/处理(记住这是一个非常图形密集的应用程序)。

编辑 :已经有两个答案有相互矛盾的答案,这是我不确定要切换的地方。 有没有人对这些差异有任何统计数据?

您当然可以使用Pens和Brushes类来为您提供已由运行时创建的对象。

例如,如果您想要一个标准颜色笔 ,您可以这样做:

 var pen = Pens.Red; 

同样,如果你只想要标准的实心画笔颜色,你可以用画笔做同样的事情:

 var brush = Brushes.Red 

使用这些,您无需担心清理,处理或其他方式。

如果您想要自己创建不同的颜色,例如使用不同的alpha组件或渐变画笔,那么您仍然需要自己创建并适当地清理它们。

编辑:

创建和处理一个由100,000个新笔组成的数组,在我古老的旧机器上花了大约半秒钟,在调试模式下运行测试应用程序。

这相当于每笔约5微秒 。 只有你可以决定它是否足够快。 我猜想这个时间对于你的其他行动来说可能是微不足道的。

您只会通过测试特定应用程序来了解性能影响,但是在应用程序的生命周期中保留一些笔似乎没有问题。 第一次调用Pens.Black ,它会创建一个黑色笔并缓存它。 对于将来的调用,您将获得相同的对象,并且它从未明确处理(Pens.Black.Dispose()实际上会抛出exception)。 但是,您不希望盲目地创建笔并在应用程序结束时让它们被丢弃,因为您将泄漏非托管内存。 根据应用程序中的使用模式,可以想到几个选项。

为每个对象提供一个私人笔,该笔在设置ForeColor时创建,并重复用于所有绘画。 你应该使你的对象IDisposable,以便它可以正确处置该笔。

如果你使用相对较少的不同颜色,但很多对象使用每种颜色,你可能不希望每个对象都持有它自己的笔。 创建一些保存Dictionary缓存类,并通过PenCache.GetPen(ForeColor)它们移出。 就像使用Pens.Black一样,你现在可以忘记处理它们了。 如果您简单地使用颜色,然后再不需要它,则会出现问题。 Pen被缓存了,所以你永远坚持在记忆中。 您可以保留Dictionary> ,允许缓存的笔最终被垃圾收集,如果它们不再需要的话。

最后一个选项可能是最好的通用解决方案,避免无偿创建和处置,同时让垃圾收集器确保孤立的笔不会造成太多内存故障。 当然,在您的特定情况下,它可能不会更好。

在需要多次操作的操作中重复使用相同的PenBrushusing每次处理它们要快得多。

我肯定会说尽可能多地重用它们是个好主意(之前我已经看过代码比较,但我现在无法找到它们),但是当你真正完成它时, 记得将它们丢弃。他们。

是的,最好像@fantius一样测试,你可能会发现它并不重要 – 虽然我很想让它们保持打开状态,好像我没记错它们是非托管资源,可能会耗尽你的记忆。 如果您每次都要重新创建它们,则需要特别注意妥善处理它们以避免内存泄漏。

好的,这是一个老问题,它可能对某些人来说是新的,并且有一个简单直观的解决方案,然后是详细的解释。

“我是否应该坚持绘画资源,比如钢笔和画笔,比预制我的绘画操作所需的时间更长”,问题会被重新定义? 答案是否定的,你不应该在这种情况下; 但为什么,这意味着什么?

当您绘制到图形对象时,您正在为您创建的每个绘制对象使用内存密集型资源,无论是笔,画笔,路径,矩阵等。

是的,创建笔,创建画笔等进行绘画并执行myPen.dispose()并立即执行以下操作:通过将该对象树设置为null(例如(myPen = null;))来释放对已处置对象的所有对象引用收集器释放这些对象持有的非托管内存,而不必等待对象finalize()的调用。 请参阅: 垃圾收集 ,以获取有关垃圾收集如何在C#中工作的更多信息。

在完成“ 使用 ”它们时创建太多这些IDisposable类对象并且不释放这些对象会对程序的操作产生严重后果,例如导致可能的堆栈和堆内存exception,从而导致必须对不必要的对象进行排序,从而导致CPU使用率增加如果不加以检查,可能导致性能降低甚至运行时崩溃。 有关更多信息,请参阅堆栈和堆 。

故事的道德释放您不再需要的资源; 如果你必须保留资源,那么当你“保持资源”希望避免负面后果时,可以测试对性能的潜在影响。

经验法则:最重要的是,确保在退出“绘制事件”例程后释放所有资源。 优选地,当您的每个绘制事件方法都完成时,该方法隐式创建的任何资源也应该完成。 惩罚,任何对象引用*传递给那些方法,如笔和画笔等将被保持,直到调用基础对象finalize,这比必要的长,并且可以被认为是定义的一般术语中的内存泄漏。 *传递太多引用等于不可用内存的时间比预期或假设的时间长。

注意:小心地将对象引用(如笔和画笔)传递给方法,如果这样做,则在类级别实现IDisposable接口以执行绘制。 您的作业变得更加昂贵,并建议您查看IDisposable界面 。

快乐的画!