Obtendo a imagem do ResourceManager GetObject - ligue para sempre ou armazenar o resultado?

StackOverflow https://stackoverflow.com/questions/4048534

  •  27-09-2019
  •  | 
  •  

Pergunta

Digamos que eu tenho que mostrar alguns gráficos sobre algum controle. Mas haverá três imagens com base em alguma condição. Três bitmap são adicionados no arquivo de recursos.

Então, eu os recupero ligando para o ResourceManager.getObject.

A questão é que, deveria ser:

  1. Toda vez que tenho que mudar de imagem, chamo GetObject para obtê -la e atribuir ao controle ou
  2. Mantenha o resultado do GetObject para cada imagem no início, para que haja apenas três chamadas para o GetObject. Atribua a imagem das minhas variáveis.

Fazer 1) parece produzir muita alça GC quando visto com o CLR Profiler. Na esperança de conhecer qualquer efeito colateral ruim de 2).

Muito obrigado.

Foi útil?

Solução

Cada chamada para GetObject lerá a imagem da montagem e a carregará em um Bitmap objeto.

Chamá -lo muitas vezes criará uma sobrecarga significativa; Você deve armazenar as imagens.

Outras dicas

Apenas uma outra coisa a apontar para chamar "ResourceManager.getObject" cada vez que você precisar usar uma imagem da Recursos é que ela parece criar um novo identificador do Windows a cada vez. No seu caso, provavelmente não é grande coisa, mas se você se apegar a eles por um tempo, como fizemos, isso pode causar um problema.

Tínhamos um DataGridView de que estávamos empurrando imagens de recursos para diferentes campos da grade e, quando essa grade aumentou mais de 3000 linhas, estávamos realmente excedendo o máximo de alças do Windows permitido para um programa de 32 bits.

O erro parecia uma exceção de argumento aleatório com a mensagem "O parâmetro não é válido". Demorou algumas horas pensando que tínhamos um vazamento de memória, mas finalmente encontramos o que carregamos essa GUI com essa grade, as manipulares de aplicativos passaram de 700-1000 para mais de 10k antes mesmo de terminar de carregar e travarem todo o programa e não conseguiram se recuperar. Então eu recomendo a opção 2 aqui.

Eu também implementei o "Leia uma vez depois armazene em variável" conceito nas minhas aulas.

Para dar um exemplo, aqui está um trecho do meu código:

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

Talvez isso ajude alguém algum dia.

o Documentação do MSDN afirma que o valor do recurso é retornado pelo ResourceManager.getObject. Como parece que os bitmaps individuais não mudam em tempo de execução, o único lado que vejo a abordar o número 2 é que sua pegada de memória será um pouco maior.

Eu tenho um aplicativo Winforms que usa muitas instâncias das mesmas formas, cada uma com muitas imagens e ícones para menus e botões e tal. Todas essas imagens são armazenadas na gerência automática [ProjectName].Properties.Resources classe.

Percebi que o uso da memória era terrivelmente alto; Depois de apenas 10 ou mais formas, ele estava usando muitas centenas de MBs de memória e cruzaria mais de 1 GB facilmente após várias outras instâncias. Eu rastreei a questão para o ResourceManager.GetObject método. o GetObject O método retorna uma nova instância de todos os objetos solicitados, o que me pareceu errado.

Em vez de deixar todas essas instâncias de imagens absorverem a memória apenas para cair do escopo, por que não reutilizá -las para futuras instâncias de forma? Então eu criei um costume CachedResourceMananger classe e substituiu o GetObject Métodos para retornar instâncias em cache dos objetos solicitados.

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

Em seguida, modifiquei a propriedade e o campo do Gerenciador de Recursos no Auto Generado [ProjectName].Properties.Resources classe para usar o gerenciador de recursos personalizado, substituindo global::System.Resources.ResourceManager com 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

Esse uso reduzido de memória drasticamente e também melhorou bastante os tempos de carregamento para novas instâncias de formulário.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top