【问题标题】:Getting Image by ResourceManager GetObject — Call it everytime or store the result?通过 ResourceManager GetObject 获取图像 - 每次调用它还是存储结果?
【发布时间】:2010-10-29 02:25:09
【问题描述】:

假设我必须在某个控件上显示一些图形。但是会根据某些条件切换三个图像。在资源文件中添加了三个位图。

所以,我通过调用 ResourceManager.GetObject 来检索它们。

问题是,应该是:

  1. 每次我必须切换图像时,我都会调用 GetObject 来获取它并分配给控件 或
  2. 在开始时为每个图像保存 GetObject 的结果,以便对 GetObject 的调用只有 3 次。而是从我的变量中分配图像。

使用 CLR Profiler 查看时,执行 1) 似乎会产生大量 GC 句柄。 希望知道 2) 的任何不良副作用。

非常感谢。

【问题讨论】:

    标签: c# resourcemanager


    【解决方案1】:

    GetObject 的每次调用都会从程序集中读取图像并将其加载到Bitmap 对象中。

    多次调用它会产生很大的开销;您应该存储图像。

    【讨论】:

      【解决方案2】:

      我有一个 WinForms 应用程序,它使用相同表单的许多实例,每个实例都有许多图像和图标,用于菜单和按钮等。所有这些图像都存储在自动生成的[ProjectName].Properties.Resources 类中。

      我注意到内存使用率非常高;在仅 10 个左右的 Form 实例之后,它使用了数百 MB 的内存,并且在多个实例之后很容易跨越 1+ GB。我将问题追溯到ResourceManager.GetObject 方法。 GetObject 方法返回请求的每个对象的新实例,这对我来说似乎是错误的。

      与其让所有这些图像实例占用内存而超出范围,为什么不将它们重用于未来的 Form 实例呢?所以我创建了一个自定义的CachedResourceMananger 类并覆盖了GetObject 方法以返回所请求对象的缓存实例。

       /// <summary>
      /// A custom Resource Manager that provides cached instances of objects.
      /// This differs from the stock ResourceManager class which always
      /// deserializes and creates new instances of every object.
      /// After the first time an object is requested, it will be cached
      /// for all future requests.
      /// </summary>
      public class CachedResourceManager : System.Resources.ResourceManager
      {
          /// <summary>
          /// A hashtable is used to store the objects.
          /// </summary>
          private Hashtable objectCache = new Hashtable();
      
          public CachedResourceManager(Type resourceSource) : base(resourceSource)
          {
          }
      
          public CachedResourceManager(string baseName, Assembly assembly) : base(baseName, assembly)
          {
          }
      
          public CachedResourceManager(string baseName, Assembly assembly, Type usingResourceSet) : base(baseName, assembly, usingResourceSet)
          {
          }
      
          public CachedResourceManager() : base()
          {
          }
      
          /// <summary>
          /// Returns a cached instance of the specified resource.
          /// </summary>
          public override object GetObject(string name)
          {
              return GetObject(name, null);
          }
      
          /// <summary>
          /// Returns a cached instance of the specified resource.
          /// </summary>
          public override object GetObject(string name, CultureInfo culture)
          {
              // Try to get the specified object from the cache.
              var obj = objectCache[name];
      
              // If the object has not been cached, add it
              // and return a cached instance.
              if (obj == null)
              {
                  objectCache[name] = base.GetObject(name, culture);
                  obj = objectCache[name];
              }
      
              return obj;
          }
      }
      

      然后我修改了自动生成的[ProjectName].Properties.Resources 类中的资源管理器属性和字段以使用自定义资源管理器,将global::System.Resources.ResourceManager 替换为CachedResourceManager

      internal class Resources
      {
          private static CachedResourceManager resourceMan;
      
          private static global::System.Globalization.CultureInfo resourceCulture;
      
          [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
          internal Resources() {
          }
      
          /// <summary>
          ///   Returns the cached ResourceManager instance used by this class.
          /// </summary>
          [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
          internal static CachedResourceManager ResourceManager 
          {
              get {
                     if (object.ReferenceEquals(resourceMan, null))
                     {
                        CachedResourceManager temp = new CachedResourceManager("Project.Properties.Resources", typeof(Resources).Assembly);
                        resourceMan = temp;
                     }
                     return resourceMan;
                  }
          }
      
          // Image/object properties for your resources
      
      } // End of resources class
      

      这大大减少了内存使用量,还大大缩短了新表单实例的加载时间。

      【讨论】:

        【解决方案3】:

        关于每次需要使用资源中的图像时调用“ResourceManager.GetObject”的另一件事是它似乎每次都创建一个新的 Windows 句柄。在你的情况下可能没什么大不了的,但如果你像我们一样坚持一段时间,可能会导致问题。

        我们有一个 DataGridView,我们将资源中的图像推送到网格的不同字段中,当该网格超过 3000 行时,我们实际上超过了 32 位程序允许的最大 Windows 句柄。

        错误出现随机参数异常,并显示消息“参数无效”。花了几个小时以为我们有内存泄漏,但最终发现我们用那个网格加载了这个 GUI,应用程序句柄在它完成加载之前从 700-1000 到超过 10K,并且会使整个程序崩溃并且无法恢复。所以我在这里推荐选项 2。

        【讨论】:

          【解决方案4】:

          我还在我的课程中实现了"read once then store in variable" concept

          举个例子,下面是我的代码的摘录:

          internal static class MyResourcesHolder
          {
              private static Image _i1;
              private static Image _i2;
              private static Image _i3;
              private static Image _i4;
              private static Image _i5;
          
              public static Image MyImage01 => _i1 ?? (_i1 = Resources.MyImage01);
              public static Image MyImage02 => _i2 ?? (_i2 = Resources.MyImage02);
              public static Image MyImage03 => _i3 ?? (_i3 = Resources.MyImage03);
              public static Image MyImage04 => _i4 ?? (_i4 = Resources.MyImage04);
              public static Image MyImage05 => _i5 ?? (_i5 = Resources.MyImage05);
          }
          

          也许有一天这会对某人有所帮助。

          【讨论】:

            【解决方案5】:

            MSDN documentation 表示资源的值由 ResourceManager.GetObject 返回。由于听起来各个位图在运行时不会改变,所以我认为接近 #2 的唯一缺点是你的内存占用会更大一些。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-01-14
              • 2011-05-28
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-08-01
              相关资源
              最近更新 更多