Хранение данных в коде


17

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

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

Я делал это раньше, потому что наборы данных были относительно небольшими, а объекты - довольно простыми. Сейчас я работаю с чем-то, что будет иметь более сложные объекты (по 5-10 свойств каждый, некоторые из них являются словарями) и всего около 200 объектов.

Сами данные меняются очень редко, а когда они меняются, это даже не так важно. Так что переходить на следующую версию совершенно нормально.

Я планирую использовать T4, ERB или другое шаблонное решение, чтобы превратить мой источник данных в нечто, что статически хранится в сборке.

Кажется, мои варианты

  1. Храните данные в XML. Скомпилируйте файл XML как ресурс сборки. Загружайте данные по мере необходимости, сохраняйте загруженные данные в словарь для повторного использования производительности.
  2. Создайте некоторый статический объект или объекты, которые инициализируются при запуске.

Я почти уверен, что понимаю последствия для варианта 1. По крайней мере, я догадываюсь, что у него не будет звездной производительности.

Что касается варианта 2, я не знаю, что делать. Я не знаю достаточно о внутренностях .NET Framework, чтобы знать, как на самом деле хранить эти данные в коде C # и как их инициализировать. Я изучил использование .NET рефлектора, чтобы увидеть, как это System.Globalization.CultureInfo.GetCulture(name)работает, поскольку на самом деле этот рабочий процесс очень похож на то, что я хочу. К сожалению, этот след закончился extern, так что никаких намеков там нет. Инициализация статического свойства со всеми данными, как в моем примере, путь? Или было бы лучше создать объекты по требованию и затем кэшировать их, как это?

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

Преимущество их одновременного создания в статическом члене состоит в том, что вы можете использовать LINQ или что-либо еще, чтобы просматривать все объекты и запрашивать их, если хотите. Хотя я подозреваю, что это приводит к снижению производительности при запуске. Я надеюсь, что кто-то имеет опыт в этом и может поделиться своим мнением!


5
200 предметов? Я не думаю, что вам нужно беспокоиться о производительности, особенно если это единовременные расходы.
svick

1
У вас есть другой вариант, который заключается в том, чтобы сохранить объекты в коде, а затем передать ссылку на сквозное внедрение зависимостей как обычно. Таким образом, если вы хотите изменить способ их построения, у вас нет статических ссылок, указывающих прямо на них отовсюду.
Эми Бланкеншип

@svick: Это правда. Я, наверное, слишком осторожен с производительностью.
сообщение

@AmyBlankenship Я бы всегда использовал вспомогательные методы, но DI - хорошая идея. Я не думал об этом. Я попробую и посмотрю, нравится ли мне шаблон. Благодарность!
сообщение

« Сами данные меняются очень редко, а когда они действительно меняются, это даже не так важно». Скажите это боснийцам, сербам, хорватам, украинцам, южному Судану, бывшим южно-йеменцам, бывшим Советам и др. Расскажите об этом каждому программисту, который имел дело с переходом на евро, или программистам, которые могут столкнуться с потенциальным выходом Греции из нее. Данные принадлежат структурам данных. XML-файлы хорошо работают и могут быть легко изменены. То же самое базы данных SQL.
Росс Паттерсон

Ответы:


11

Я бы выбрал первый вариант. Это просто и легко читать. Кто-то еще, глядя на ваш код, сразу поймет это. Также будет проще обновить ваши данные XML, если это будет необходимо. (Кроме того, вы можете позволить пользователю обновлять их, если у вас есть хороший внешний интерфейс и файлы хранятся отдельно)

Оптимизируйте его только в случае необходимости - преждевременная оптимизация - это зло :)


3
Если вы пишете на C #, значит, вы работаете на платформе, которая может быстро анализировать небольшие XML-подсветки. Так что не стоит беспокоиться о проблемах производительности.
Джеймс Андерсон

7

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

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

Чтобы сделать его немного более эффективным, вы можете загрузить их все сразу, например так:

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

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

Если это приложение WPF, словарь ресурсов - гораздо более очевидное решение.


2

Решение действительно: хотите ли вы, чтобы только разработчик (или кто-либо, имеющий доступ к системе сборки) изменил данные, когда они нуждаются в изменении, или если конечный пользователь или, возможно, кто-то в организации конечного пользователя сможет изменить данные?

В последнем случае я бы предположил, что файл в читаемом и редактируемом формате будет храниться в месте, где пользователь может получить к нему доступ. Очевидно, что когда вы читаете данные, вы должны быть осторожны; все, что вы найдете, нельзя доверять как действительные данные. Я бы предпочел JSON XML Мне легче понять и понять правильно. Вы можете проверить, есть ли редактор, доступный для формата данных; например, если вы используете plist на MacOS X, то у каждого пользователя, который должен когда-либо приближаться к данным, будет свой достойный редактор на своем компьютере.

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


1

Наилучшим примером этого является, вероятно, WPF ... Ваши формы написаны на XAML, который в основном является XML, за исключением того, что .NET может очень быстро преобразовать его в представление объекта. Файл XAML скомпилирован в файл BAML и сжат в файл «.resources», который затем внедряется в сборку (последняя версия .NET рефлектора может показать вам эти файлы).

Хотя не существует какой-либо замечательной документации для его поддержки, MSBuild на самом деле должна иметь возможность взять файл XAML, преобразовать его в BAML и встроить его для вас (он делает это для форм, верно?).

Итак, мой подход заключается в следующем: хранить ваши данные в XAML (это просто XML-представление графа объектов), помещать этот XAML в файл ресурсов и вставлять его в сборку. Во время выполнения захватите XAML и используйте XamlServices для его регидратации. Затем либо оберните его в статический член, либо используйте Dependency Injection, если вы хотите, чтобы он был более тестируемым, чтобы использовать его из остального кода.

Несколько ссылок, которые являются полезным справочным материалом:

Кстати, вы можете использовать файлы app.config или web.config для загрузки достаточно сложных объектов с помощью механизма настроек, если вы хотите, чтобы данные изменялись легче. И вы также можете загрузить XAML из файловой системы.


1

Я предполагаю, что System.Globalization.CultureInfo.GetCulture (имя) будет вызывать API-интерфейсы Windows для получения данных из системного файла.

Для загрузки данных в коде, как сказал @svick, если вы собираетесь загрузить 200 объектов, в большинстве случаев это не будет проблемой, если ваш код работает на некоторых устройствах с малой памятью.

Если ваши данные никогда не меняются, я просто использую список / словарь, например:

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

Но проблема в том, что вам нужно найти способ инициализации вашего словаря. Я думаю, что это болевая точка.

Таким образом, проблема меняется на «Как генерировать ваши данные?». Но это относится к тому, что вам нужно.

Если вы хотите сгенерировать данные для стран, вы можете использовать

System.Globalization.CultureInfo.GetCultures()

получите массив CultrueInfo, и вы можете инициализировать свой словарь с вашими потребностями.

И, конечно, вы можете поместить код в статический класс и инициализировать словарь в статическом конструкторе, например:

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;
        }
    }
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.