Страницы

Поиск по вопросам

воскресенье, 12 января 2020 г.

Интерполяция строк в C# 6 без использования $

#c_sharp #строки #интерполяция


В конфигурационном JSON файле используются строки следующего вида:


{
  "logFilePattern" : "{yyyy}.{mm}.{dd}.log"
}


Для замены используется множество разных текстовых заполнителей: вычисляемые пути
файловой системы, которые зависят от пользователя и т.д.. Предполагается их замена
и для этого в предыдущих версиях C# использовалось составное форматирование (могу ошибаться
в термине) таким образом:


string name = "Fred";
Console.WriteLine(String.Format("Name = {0}", name));
// output: "Name = Fred"


С выходом C# 6 версии появилась возможность использовать именованные аргументы (могу
ошибаться в термине) таким образом:


string name = "Fred";
Console.WriteLine($"Name = {name}");
// output: "Name = Fred"


Вопрос: как интерполировать строку с именованными аргументами сохраненую в переменной?


string stringPattern = "Name = {name}";
string name = "Fred";
// ?



Нашел здесь что это не возможно, т.к. код:


string name = "Fred";
string name2 = $"Name = {name}";


, компилятором приводится к:


string name = "fred";
string name2 = String.Format("Name = {0}", name);


Другим словами, это просто синтаксический сахар.
    


Ответы

Ответ 1



UPD Если весь вопрос в том, как сгенерировать имя лог файла, то может проще обойтись таким способом? В конфигурации задаем валидный с т.з. .NET формат для даты: { "logFilePattern" : "yyyy.MM.dd.log" } Затем в коде выкусываем его и получаем имя файла: const string extension = ".log"; var datePattern = logFilePattern.Remove(logFilePattern.IndexOf(extension), extension.Length); DateTime date = DateTime.UtcNow; var logFileName = date.ToString(datePattern) + extension; О задаче в общем Наивная реализация может состоять просто в замене значения: string stringPattern = "Name = {name}"; string name = "Fred"; string formatted = stringPattern.Replace($"{{nameof(name)}}", name); Тут есть два недостатка: код неуниверсальный (хотелось бы иметь отдельный метод, типа FormatString(pattern, value1, value2, ...)) не учитывается возможное эскапирование фигурных скобок (строка "{{name}}" не является шаблоном, однако замена будет произведена) при наличии нескольких значений мы будем плодить лишние строки Метод вида FormatString(pattern, value1, value2, ...), насколько я понимаю, нереален, поскольку нет способа в вызываемом методе узнать имена параметров, с которыми он вызывался. Поэтому можно остановиться на, например, словаре: public static string FormatString(string pattern, IDictionary values) Эскапирование можно обработать регулярным выражением, но учитывая, что параметров может быть несколько, это тоже может быть излишне тяжелой операцией. Поэтому реализация в общем-то зависит от желаемой степени оптимизированности, в идеале надо вручную проходить char[]/List и менять его на ходу. На ночь глядя получилось так: public static string FormatString(string pattern, IDictionary values) { var buffer = pattern.ToList(); int replaceStartIndex = -1; int replaceEndIndex = -1; int index = 0; while (index < buffer.Count) { var character = buffer[index]; if (character == '{') { if (replaceStartIndex == -1) { replaceStartIndex = index; } else { replaceStartIndex = -1; } } else if (character == '}') { if (replaceEndIndex == -1) { replaceEndIndex = index; } else { replaceEndIndex = -1; } } if (replaceStartIndex > -1 && replaceEndIndex > -1) { var key = new string(buffer.Skip(replaceStartIndex + 1).Take(replaceEndIndex - replaceStartIndex - 1).ToArray()); var value = values[key]; var stringValue = value?.ToString(); buffer.RemoveRange(replaceStartIndex, replaceEndIndex - replaceStartIndex + 1); if (stringValue != null) { buffer.InsertRange(replaceStartIndex, stringValue.ToCharArray()); index = replaceStartIndex + stringValue.Length; } else { index = replaceStartIndex; } replaceStartIndex = -1; replaceEndIndex = -1; } else { index++; } } return string.Join("", buffer); } Проверка: static void Main(string[] args) { string stringPattern = "Name = {name}, Age = {age}, Null = {nullString}, Escape = {{escape}}"; string name = "Fred"; int age = 42; string nullString = null; string escape = "You shouldn't see this"; var values = new Dictionary() { [nameof(name)] = name, [nameof(age)] = age, [nameof(nullString)] = nullString, [nameof(escape)] = escape }; string formatted = FormatString(stringPattern, values); Console.WriteLine(formatted); } Вывод: Name = Fred, Age = 42, Null = , Escape = {{escape}}

Ответ 2



Вот тут сказано, что приведенная в топике строка является типом System.FormattableString. По поводу которого читаем что можно получить по индексу нужный инжектированный параметр. И просто можем сформировать новую строку со своим инжектом. Это возможно в рамках передаваемой интерполированной строки в функцию, например.. Возможно таким способом получить нужный результат?!

Комментариев нет:

Отправить комментарий