Rep*_*Man 10 .net c# system.drawing gdi+ winforms
对于我的一些winforms应用程序,我需要创建一大堆GDI +对象(画笔,笔,字体等)并一遍又一遍地使用它们.我创建了一个贫民窟缓存单身人士来完成我需要的东西,但代码味道压倒性......
public sealed class GraphicsPalette
{
public static readonly GraphicsPalette Instance = new GraphicsPalette();
static GraphicsPalette()
{
}
private Dictionary<Color, Brush> solidBrushes;
//multithreading
private object brushLock;
private GraphicsPalette()
{
solidBrushes = new Dictionary<Color, Brush>();
brushLock = new object();
}
public Brush GetSolidBrush(Color color, int alpha)
{
return GetSolidBrush(Color.FromArgb(alpha, color));
}
public Brush GetSolidBrush(Color color)
{
if (!solidBrushes.ContainsKey(color))
{
lock (brushLock)
{
if (!solidBrushes.ContainsKey(color))
{
Brush brush = new SolidBrush(color);
solidBrushes.Add(color, brush);
return brush;
}
}
}
return solidBrushes[color];
}
}
Run Code Online (Sandbox Code Playgroud)
OnPaint()调用它们时都重新实例化它们?Brush会调用每个对象的终结器,从而释放任何非托管资源?如果这是重复,我道歉,但我没有发现任何类似的问题.
不会发生内存泄漏,但是最好在您认为合适的情况下释放GDI +对象。操作系统中它们的数量有限,因此您可能会在您的应用程序和其他应用程序中引起渲染问题。要提到的另一件事是GDI +对象(字体等)不能同时被2个以上的线程使用(可能会抛出一些难以重现的异常)。您可能对实际GDI +对象创建时间与可能的排他锁定延迟的一些度量感兴趣。“过早的优化是万恶之源”©Donald Knuth
实际上,对我来说,在每个绘画周期中进行一些GDI +对象缓存是可行的。客户端代码可能如下所示:
class Visual
{
public void Draw()
{
using (new GraphicsPalette()) {
DrawHeader();
DrawFooter();
}
}
private void DrawHeader() {
var brush = GraphicsPalette.GetSolidBrush(Color.Green);
...
}
public void DrawFooter() {
using (new GraphicsPalette()) { // ensures palette existence; does nothing if there is a palette on the stack
var brush = GraphicsPalette.GetSolidBrush(Color.Green); // returns the same brush as in DrawHeader
...
}
}
}
Run Code Online (Sandbox Code Playgroud)
因此,我们需要GraphicsPalette忽略嵌套构造,并为给定线程返回相同的画笔。建议的解决方案:
public class GraphicsPalette : IDisposable
{
[ThreadStatic]
private static GraphicsPalette _current = null;
private readonly Dictionary<Color, SolidBrush> _solidBrushes = new Dictionary<Color, SolidBrush>();
public GraphicsPalette()
{
if (_current == null)
_current = this;
}
public void Dispose()
{
if (_current == this)
_current = null;
foreach (var solidBrush in _solidBrushes.Values)
solidBrush.Dispose();
}
public static SolidBrush GetSolidBrush(Color color)
{
if (!_current._solidBrushes.ContainsKey(color))
_current._solidBrushes[color] = new SolidBrush(color);
return _current._solidBrushes[color];
}
}
Run Code Online (Sandbox Code Playgroud)