Получение изображения с помощью ResourceManager GetObject - вызывать его каждый раз или сохранить результат? - PullRequest
6 голосов
/ 29 октября 2010

Допустим, мне нужно показать графику на каком-то элементе управления.Но будет три изображения, переключаемые в зависимости от какого-либо условия.В файл ресурсов добавлено три растровых изображения.

Итак, я получаю их, вызывая ResourceManager.GetObject.

Вопрос в том, должно ли это быть:

  1. Каждый раз, когда мне нужно переключить изображение, я вызываю GetObject, чтобы получить его и назначаю элементу управления, или
  2. удерживаем результат GetObject для каждого изображения в начале, так что когда-либо будет только 3 вызова GetObject.Вместо этого назначьте изображение из моих переменных.

Выполнение 1), по-видимому, приводит к большой обработке GC Handle при просмотре с помощью CLR Profiler.Надеюсь узнать любой плохой побочный эффект 2).

Большое спасибо.

Ответы [ 5 ]

7 голосов
/ 29 октября 2010

Каждый вызов GetObject будет считывать изображение из сборки и загружать его в Bitmap объект.

Многократный вызов вызовет значительные накладные расходы; Вы должны хранить изображения.

4 голосов
/ 23 мая 2013

Еще одна вещь, на которую следует обращать внимание при вызове ResourceManager.GetObject каждый раз, когда вам нужно использовать изображение из ресурсов, это то, что каждый раз создается новый дескриптор Windows.В вашем случае это, вероятно, не имеет большого значения, но если вы будете держаться за них какое-то время, как мы, это может вызвать проблему.

У нас был DataGridView, в который мы помещали изображения из ресурсов в разные областисетка и когда эта сетка поднялась более чем на 3000 строк, мы фактически превысили максимально допустимые дескрипторы Windows для 32-битной программы.

Произошла ошибка. Произошло случайное исключение аргумента с сообщением «Параметр недействителен».Потребовалось несколько часов, чтобы подумать, что у нас произошла утечка памяти, но, наконец, мы обнаружили, что мы загрузили этот графический интерфейс с этой сеткой, дескрипторы приложений поднялись с 700-1000 до более 10 КБ, прежде чем он даже завершил загрузку и мог привести к сбою всей программы и не смог восстановиться.Поэтому я рекомендую вариант 2 здесь.

2 голосов
/ 18 ноября 2015

Я также реализовал концепцию «читать один раз, затем сохранять в переменной» в моих классах.

Для примера приведу выдержку из моего кода:

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

Может быть, это кому-нибудь когда-нибудь поможет.

1 голос
/ 29 октября 2010

В документации MSDN указано, что значение ресурса возвращается ResourceManager.GetObject. Поскольку кажется, что отдельные растровые изображения не меняются во время выполнения, единственный недостаток, который я вижу в подходе №2, заключается в том, что объем памяти будет немного больше.

0 голосов
/ 05 апреля 2018

У меня есть приложение WinForms, в котором используется множество экземпляров одних и тех же форм, в каждом из которых много изображений и значков для меню, кнопок и тому подобного. Все эти изображения хранятся в автоматически сгенерированном классе [ProjectName].Properties.Resources.

Я заметил, что использование памяти было ужасно высоким; только после примерно 10 экземпляров Form он использовал сотни сотен мегабайт памяти и легко пересекал бы 1+ Гбайт еще через несколько экземпляров. Я проследил проблему до 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

Это значительно сократило использование памяти, а также значительно улучшило время загрузки для новых экземпляров форм.

...