代码之家  ›  专栏  ›  技术社区  ›  Thomas Levesque

在.NET中创建字体对象的成本

  •  6
  • Thomas Levesque  · 技术社区  · 16 年前

    在我正在开发的应用程序中,我们使用devexpress xtragrid控件,它具有 RowCellStyle 允许自定义每个单元格样式的事件。此事件的事件处理程序通常如下所示:

    private gridView1_RowCellStyle(object sender, RowCellStyleEventArgs e)
    {
        if (/* Some condition */)
        {
            e.Appearance.Font = new Font(gridView1.Appearance.Font, FontStyle.Bold);
        }
    }
    

    每次呈现单元格时都会调用此处理程序,因此它可以创建大量 Font 实例。所以我想知道这样做的代价…我做了一些实验,似乎每次都会创建一个新的hfont句柄。我应该担心吗?对资源使用的影响有多大?

    如果它对性能有重大影响,是否应该 FontCache 等级还是类似的?

    注意:我知道如何解决这个问题(我只需要创建一次字体,然后每次都重新使用它),我的问题实际上是创建许多hfont句柄的成本。

    3 回复  |  直到 12 年前
        1
  •  6
  •   Marc Gravell    16 年前

    测试一下;我通过重用获得了双倍的性能(在发行版中,重用=3000毫秒,重新创建=4900毫秒)

    using System.Windows.Forms;
    using System.Drawing;
    using System.Diagnostics;
    static class Program
    {
        static void Main()
        {
            Button btn1, btn2;
            Form form = new Form
            {
                Controls = {
                    (btn1 = new Button { Dock = DockStyle.Bottom, Text = "reuse" }),
                    (btn2 = new Button { Dock = DockStyle.Bottom, Text = "recreate"})
                }
            };
            btn1.Click += delegate
            {
                var watch = Stopwatch.StartNew();
                using (var gfx = form.CreateGraphics())
                using (var font = new Font(SystemFonts.DefaultFont, FontStyle.Bold))
                {                
                    gfx.Clear(SystemColors.Control);
                    for (int i = 0; i < 10000; i++)
                    {
                        gfx.DrawString("abc", font, SystemBrushes.ControlText, i % 103, i % 152);
                    }
                }
                watch.Stop();
                form.Text = watch.ElapsedMilliseconds + "ms";
            };
            btn2.Click += delegate
            {
                var watch = Stopwatch.StartNew();
                using (var gfx = form.CreateGraphics())
                {
                    gfx.Clear(SystemColors.Control);
                    for (int i = 0; i < 10000; i++)
                    {
                        using (var font = new Font(SystemFonts.DefaultFont, FontStyle.Bold))
                        {
                            gfx.DrawString("abc", font, SystemBrushes.ControlText, i % 103, i % 152);
                        }
                    }
                }
                watch.Stop();
                form.Text = watch.ElapsedMilliseconds + "ms";
            };
            Application.Run(form);
    
        }
    }
    
        2
  •  2
  •   Matt Breckon    16 年前

    字体实现IDisposable-完成后应确保调用Dispose。

    它是一个非托管资源,因此操作系统最终可能会耗尽资源来为您提供更多的对象。

    检查taskmanager中的gdi句柄计数,看它是否持续增加。

        3
  •  2
  •   meklarian    16 年前

    重用几乎总是比按需快,但重用或缓存字体对象的能力可能有所不同。

    如果要重用字体对象并将不同的字体对象与每个控件关联,则会增加超出gdi句柄限制的可能性。这将损害给定用户使用的所有应用程序。

    理想情况下,您应该在应用程序/流程级别缓存字体对象,并且只缓存必要的最小设置(最多可能是十几个)。此外,缓存一个小的共享字体集和其他gdi对象允许您更好地处理一些棘手的消息,如wm_settingchange。在示例中,您正在修改默认值,当用户更改方案或Windows中的默认显示字体时,可能会遇到绘图错误。在wm_设置更改期间,您将考虑释放缓存的副本,并初始化或准备新的字体对象集。

    WM_SETTINGCHANGE (Windows) @ MSDN

    如果您需要有数百种不同的字体(或其他GDI对象)可用,则不应缓存字体,只应根据需要创建所需的内容。这对性能不友好,但它将帮助您的应用程序与用户可能打开的其他应用程序共存。持有数百个gdi对象将大大增加用户遇到gdi句柄限制的可能性。但是,基于引用计数的缓存可能会在这里提供一个最适合您的折衷方案,只保留最常用变体的硬拷贝。

    最后,正如MattBreckon所提到的,对所有可用的GDI对象使用IDisposable。这将帮助您避免GDI处理泄漏。