Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting Image by ResourceManager GetObject — Call it everytime or store the result?

Let's say that I have to show some graphics on some control. But there will be three images switched based on some condition. Three bitmap is added in the resource file.

So, I retrieve them by calling ResourceManager.GetObject.

The question is that, should it be:

  1. Everytime I have to switch image, I call GetObject to get it and assign to the control or
  2. hold the result of GetObject for each image at the start, so that there will only ever be 3 calls to the GetObject. Assign image from my variables instead.

Doing 1) seems to produce a lot of GC Handle when viewed with CLR Profiler. Hoping to know any bad side effect of 2).

Thanks a lot.

like image 592
lexington Avatar asked Oct 29 '10 02:10

lexington


2 Answers

Each call to GetObject will read the image from the assembly and load it into a Bitmap object.

Calling it many times will create significant overhead; you should store the images.

like image 104
SLaks Avatar answered Sep 27 '22 22:09

SLaks


I have a WinForms application which uses many instances of the same Forms, each one with many images and icons for menus and buttons and such. All of these images are stored in the auto-generated [ProjectName].Properties.Resources class.

I noticed that the memory usage was terribly high; after only 10 or so Form instances it was using many hundreds of MBs of memory, and would cross 1+ GB easily after several more instances. I traced the issue to the ResourceManager.GetObject method. The GetObject method returns a new instance of every object requested, which just seemed wrong to me.

Instead of letting all those instances of images soak up memory only to fall out of scope, why not reuse them for future Form instances? So I created a custom CachedResourceMananger class and overrode the GetObject methods to return cached instances of the requested objects.

 /// <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;
    }
}

I then modified the resource manager property and field in the auto-generated [ProjectName].Properties.Resources class to use the custom resource manager, replacing global::System.Resources.ResourceManager with 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

This reduced memory usage drastically and also greatly improved the loading times for new Form instances.

like image 27
Bobby L Avatar answered Sep 27 '22 22:09

Bobby L