C#, ConfigurationBuilder и типизированные значения
я не понимаю, почему при стандартном чтении json-файла с конфигурацией параметры конфиграции приходят мне как строки, независимо от того, как они написаны в json-файле.
Поясню.
Вот я создаю стандартный консольный проект на NET Core 8, добавляю туда зависимости Microsoft.Extensions.Configuration
и Microsoft.Extensions.Configuration.Json
Затем прямов Main'е я пишу
var builder = new ConfigurationBuilder().AddJsonFile("appSettings.json", false, false);
var configuration = builder.Build();
var rc = configuration["retryCounter"];
Затем я добавляю appSettings.json, в котором написано
{
"retryCounter": 42
}
... и в коде я получаю строку со значением "42":
И вот чего я не понимаю:
json у нас - типизированный. Да, там есть только примитивные типы, но число
"retryCounter": 42
отличатеся от строки
"retryCounter": "42"
Если я буду пользоваться своим классом, каким нибудь
MySettings.cs
с описанными в нём полями, и буду его сериализовать - десериализовать в json - то у меня будет преобразование в json и обратно, сохраняющее элементарные типы (int, string, float)В то же время, использование конфигурации и ConfigurationBuilder'а - это рекомендуемый путь. То есть, если ты делаешь не так - то тебе в приличном месте дадут по рукам.
Так почему же тогда этот рекомендуемый путь - такой ограниченный, и не отличается от работы с ini- файлом? Здесь есть какой то смысл, которого я не понимаю, или это просто "все данные в мире - это строки, и не надо нам морочить голову!"?
Ответы (1 шт):
Кратко
Причины такого подхода работы с конфигурацией (на мой взгляд):
- Универсальность. В такую структуру данных можно поместить конфигурацию любого формата, нужно лишь правильно преобразовать изначальный формат в словарь.
- Строгая типизация языка C# накладывает ограничения на хранение разнотипных данных в одном словаре. Придется либо в ущерб производительности использовать общий тип
object
для хранения значений, что приведет к boxing / unboxing значений конфигурации, либо придется изменить способ хранения данных, что может "ударить" по универсальности.
Подробнее
Внутри ConfigurationBuilder
метод .Build() возвращает ConfigurationRoot, который в свою очередь использует доступные ConfigurationProvider для получения значений конфигурации.
ConfigurationProvider
устроен таким образом, что данные конфигурации хранятся как словарь с параметрами "ключ-строка", или Dictionary<string, string?>
Поскольку C# - строго типизированный язык, то при желании хранить данные с нужной типизацией для элементарных типов придется воспользоваться Dictionary<string, object?>
, и при вытаскивании данных передавать тип, который мы ожидаем на выходе (либо использовать dynamic
). Выходит не очень практично, да и есть накладные расходы на boxing / unboxing. Ведь всем хочется получать конфигурацию максимально быстро и потребляя меньше памяти.
Также, ConfigurationProvider
- базовый абстрактный класс, который переиспользуется в более конкретных реализациях. Идея хранить все значения как пара "ключ - строка" - вполне разумная для строго типизированного языка программирования. Мы получили строгую плоскую структуру данных, где доступ к универсальному строковому значению можно получить по композитному ключу (который напоминает ключи redis cache). Структура простая, и мы получаем общий интерфейс работы с конфигурацией, который можно применять при любых других способах получения конфигурации, не только JSON
(например, из текстового файла, из БД и др.). В каком-нибудь Javascript, где типизация динамическая, хранить значения с оригинальным типом было бы уместно.