Armazenando dados no código

16

Algumas vezes no meu passado eu quis armazenar dados no código. Isso seria um dado que raramente muda e é usado em lugares onde o acesso a um banco de dados não é possível, prático ou desejável. Um pequeno exemplo seria armazenar uma lista de países. Para isso, você poderia fazer algo como:

public class Country
{
    public string Code { get; set; }
    public string EnglishName {get;set;}
}

public static class CountryHelper
{
    public static List<Country> Countries = new List<Country>
        {
            new Country {Code = "AU", EnglishName = "Australia"},
            ...
            new Country {Code = "SE", EnglishName = "Sweden"},
            ...
        };

    public static Country GetByCode(string code)
    {
        return Countries.Single(c => c.Code == code);
    }
}

Eu consegui fazer isso no passado porque os conjuntos de dados eram relativamente pequenos e os objetos bem simples. Agora estou trabalhando com algo que terá objetos mais complexos (5 - 10 propriedades cada, algumas propriedades sendo dicionários) e cerca de 200 objetos no total.

Os dados em si mudam muito raramente e, quando mudam, nem é tão importante assim. Então, rolá-lo para a próxima versão está perfeitamente bem.

Estou planejando usar o T4 ou o ERB ou alguma outra solução de modelo para transformar minha fonte de dados em algo armazenado estaticamente na montagem.

Parece que minhas opções são

  1. Armazene os dados em XML. Compile o arquivo XML como um recurso de montagem. Carregar dados conforme necessário, armazenar dados carregados em um Dicionário para desempenho de uso repetido.
  2. Gere algum tipo de objeto estático ou objetos que são inicializados na inicialização.

Tenho certeza que entendo as implicações de desempenho da opção 1. Pelo menos, meu palpite é que não teria um desempenho estelar.

Quanto à opção 2, não sei o que fazer. Eu não sei o suficiente sobre os internos do .NET framework para saber a melhor maneira de realmente armazenar esses dados em código C # e as melhores maneiras de inicializá-lo. Eu explorei o refletor .NET para ver como o System.Globalization.CultureInfo.GetCulture(name) funciona, pois esse é um fluxo de trabalho muito parecido com o que eu quero. Infelizmente essa trilha terminou em extern , então não há dicas. Está inicializando uma propriedade estática com todos os dados, como no meu exemplo, o caminho a percorrer? Ou seria melhor criar os objetos sob demanda e depois armazená-los em cache, assim?

    private static readonly Dictionary<string, Country> Cache = new Dictionary<string,Country>(); 

    public static Country GetByCode(string code)
    {
        if (!Cache.ContainsKey(code))
            return Cache[code];

        return (Cache[code] = CreateCountry(code));
    }

    internal static Country CreateCountry(string code)
    {
        if (code == "AU")
            return new Country {Code = "AU", EnglishName = "Australia"};
        ...
        if (code == "SE")
            return new Country {Code = "SE", EnglishName = "Sweden"};
        ...
        throw new CountryNotFoundException();
    }

A vantagem de criá-los de uma só vez em um membro estático é que você pode usar o LINQ ou qualquer outra coisa para examinar todos os objetos e consultá-los, se desejar. Embora eu suspeite que isso tenha uma penalidade de desempenho de inicialização. Espero que alguém tenha experiência com isso e possa compartilhar suas opiniões!

    
por mroach 30.04.2013 / 00:32
fonte

5 respostas

11

Eu iria com a opção um. É simples & fácil de ler. Alguém olhando para o seu código entenderá imediatamente. Também será mais fácil atualizar seus dados XML, se necessário. (Além disso, você pode deixar o usuário atualizá-los se tiver um bom front-end e armazenar os arquivos separadamente)

Otimize-o apenas se precisar - a otimização prematura é má:)

    
por 30.04.2013 / 06:12
fonte
7

Como esses são essencialmente pares de valores-chave, recomendo armazenar essas sequências como um arquivo de recursos incorporado em sua montagem em tempo de compilação. Você pode lê-los usando um ResourceManger . Seu código seria algo como isto:

private static class CountryHelper
{
    private static ResourceManager rm;

    static CountryHelper()
    {
        rm = new ResourceManager("Countries",  typeof(CountryHelper).Assembly);
    }

    public static Country GetByCode(string code)
    {
        string countryName = rm.GetString(code + ".EnglishName");
        return new Country { Code = code, EnglishName = countryName };
    }
}

Para torná-lo um pouco mais eficiente, você pode carregá-los todos de uma vez, assim:

private static class CountryHelper
{
    private static Dictionary<string, Country> countries;

    static CountryHelper()
    {
        ResourceManager rm = new ResourceManager("Countries",  typeof(CountryHelper).Assembly);
        string[] codes = rm.GetString("AllCodes").Split("|"); // AU|SE|... 
        countries = countryCodes.ToDictionary(c => c, c => CreateCountry(rm, c));
    }

    public static Country GetByCode(string code)
    {
        return countries[code];
    }

    private static Country CreateCountry(ResourceManager rm, string code)
    {
        string countryName = rm.GetString(code + ".EnglishName");
        return new Country { Code = "SE", EnglishName = countryName };
    }
}

Isso o ajudará muito se você precisar fazer seu aplicativo suportar vários idiomas.

Se isso acontecer de ser um aplicativo WPF, um dicionário de recursos é uma solução muito mais óbvia.

    
por 30.04.2013 / 00:57
fonte
1

O melhor exemplo disso é provavelmente o WPF ... Seus formulários são escritos em XAML, que é basicamente XML, exceto que o .NET é capaz de reidratá-lo em uma representação de objeto muito rapidamente. O arquivo XAML é compilado em um arquivo BAML e espremido no arquivo ".resources", que é incorporado ao assembly (a versão mais recente do .NET reflector pode mostrar esses arquivos).

Embora não exista uma grande documentação para suportá-lo, o MSBuild deve, na verdade, ser capaz de pegar um arquivo XAML, convertê-lo em BAML e incorporá-lo a você (ele faz isso para formulários certos?).

Portanto, minha abordagem seria: armazenar seus dados em XAML (é apenas uma representação XML de um gráfico de objeto), incluir esse XAML em um arquivo de recursos e incorporá-lo ao assembly. Em tempo de execução, pegue o XAML e use XamlServices para reidratá-lo. Em seguida, envolva-o em um membro estático ou use a Injeção de dependência, se quiser que ele seja mais testável, para consumi-lo do restante de seu código.

Alguns links que são materiais de referência úteis:

Em uma nota lateral, você pode realmente usar os arquivos app.config ou web.config para carregar objetos razoavelmente complexos através do mecanismo de configurações, se desejar que os dados sejam alterados com mais facilidade. E você também pode carregar o XAML do sistema de arquivos.

    
por 30.04.2013 / 01:02
fonte
1

Eu acho que o System.Globalization.CultureInfo.GetCulture (nome) iria chamar APIs do Windows para obter os dados de um arquivo de sistema.

Para carregar os dados no código, como o @svick disse, se você estiver prestes a carregar 200 objetos, isso não será um problema na maioria dos casos, a menos que seu código seja executado em alguns dispositivos com pouca memória.

Se seus dados nunca mudarem, minha experiência é simplesmente usar uma lista / dicionário como:

private static readonly Dictionary<string, Country> _countries = new Dictionary<string,Country>();

Mas o problema é que você precisa encontrar uma maneira de inicializar seu dicionário. Eu acho que esse é o ponto de dor.

Então o problema é alterado para "Como gerar seus dados?". Mas isso se relaciona com o que você precisa.

Se você quiser gerar dados para os países, use o

System.Globalization.CultureInfo.GetCultures()

obtenha o array CultrueInfo, e você poderá inicializar seu dicionário de acordo com suas necessidades.

E, claro, você poderia colocar o código em uma classe estática e inicializar o dicionário no construtor estático como:

public static class CountryHelper
{
    private static readonly Dictionary<string, Country> _countries;
    static CountryHelper()
    {
        _countries = new Dictionary<string,Country>();
        // initialization code for your dictionary
        System.Globalization.CultureInfo[] cts = System.Globalization.CultureInfo.GetCultures(System.Globalization.CultureTypes.AllCultures);
        for(int i=0; i < cts.Length; i++)
        {
            _countries.Add(cts[i].Name, new Country(cts[i]));
        }
    }

    public static Country GetCountry(string code)
    {
        Country ct = null;
        if(this._countries.TryGet(code, out ct))
        {
            return ct;
        } else
        {
            Log.WriteDebug("Cannot find country with code '{0}' in the list.", code);
            return null;
        }
    }
}
    
por 30.04.2013 / 03:58
fonte
1

A decisão é realmente: Você quer que apenas o desenvolvedor (ou qualquer pessoa com acesso a um sistema de compilação) modifique os dados quando precisar modificá-los, ou que um usuário final ou possivelmente alguém na organização do usuário final possa modificar os dados?

No último caso, sugiro que um arquivo em formato legível pelo usuário e editável pelo usuário seja armazenado em um local onde o usuário possa acessá-lo. Obviamente, quando você lê os dados, precisa ser cuidadoso; o que você encontrar não pode ser confiável para ser dados válidos. Eu provavelmente preferiria o JSON ao XML; Eu acho mais fácil entender e acertar. Você pode verificar se existe um editor disponível para o formato de dados; Por exemplo, se você usar uma plist no MacOS X, todos os usuários que chegarem perto dos dados terão um editor decente no computador.

No primeiro caso, se os dados fizerem parte de sua compilação, isso não fará diferença. Faça o que for mais conveniente para você. Em C ++, gravar os dados com sobrecarga mínima é um dos poucos locais em que as macros são permitidas. Se o trabalho de manutenção dos dados for feito por terceiros, você poderá enviar os arquivos de origem, editá-los e, em seguida, será responsável por verificá-los e utilizá-los em seu projeto.

    
por 30.05.2015 / 15:54
fonte

Tags