Подводный камень интерполяции строк
Рассмотрим строку:
"{0} Client called server Method: {1}) Returned: {2} Status: {3}.",
session.GetClientInfo(), request, response, status
$"{session.GetClientInfo()} Client called server Method: {request}) Returned: {response} Status: {status}."
Оказывается, что в request, response, status может оказаться какая-либо пара (а может и одинарная, не проверял) фигурных скобок. В этом случае повторное форматирование может привести к ошибкам.
Когда это происходит? Например если у нас есть функция типа этой:
static void outMessage(LogFilter type, LogLevel level, string text, params object[] args)
{
Logger logger = GetLoggerByType(type);
logger.write(new LogMessage(level, type, string.Format(text, args)));
}
Если в качестве аргумента будет строка с фигурными скобками, внутри которых есть выражение с неопознаваемым форматом, например:
{{ "useBindlessRpc": true, "meteringLevel": "METERING_LEVEL_LEGACY", "clientSdkInfo": { "sdkName": "BGS C++ SDK", "sdkVersion": { "majorVersion": 6, "minorVersion": 2, "patchVersion": 0, "versionString": "6.2.0" }, "protocolVersion": { "majorVersion": 3, "minorVersion": 11, "patchVersion": 10, "versionString": "3.11.10-44dad063b5" } } }}
Почему такая функция существует вообще:
- Наверное создавалась во времена, до введения интерполяции
- Поддержка обоих вариантов
- Возможно предполагалась поддержка комбинированного варианта (не очень хорошая идея, потому как в этом случае ошибки точно не избежать)
Откуда могут взяться фигурные скобки в переменной:
- Ввод с клавиатуры
- результат сериализации
- из любого другого места
Также возможно проблема может возникнуть из-за этого при наличии внутри скобок:
Prior to C# 10, if an interpolated string has the type string, it's typically transformed into a String.Format method call. The compiler may replace String.Format with String.Concat if the analyzed behavior would be equivalent to concatenation.
Помогает ли экранирование скобок? - не пробовал, но в коде C# запрещает их экранировать
Вопрос: Что делать в таком случае?
UPD: Вот на этой платформе , а также на данной машине ошибка не возникает. Можно предположить что эта ошибка зависит от "Бог знает чего". Я остановился на предположении, что зависит от кодировки. Вот по сообщению ошибки требуется ASCII, хотя вроде как по умолчанию UTF16. Почему там именно такая кодировка требуется - я не знаю. Может это не правильная ошибка вовсе. Устал тыкать дебаггер. Если кому интересно - можете сами поковырять.
Ответы (2 шт):
Изменить метод, например вот так:
static void outMessage(LogFilter type, LogLevel level, string text, params object[] args)
{
Logger logger = GetLoggerByType(type);
string result = args.Length > 0 ? string.Format(text, args) : text;
logger.write(new LogMessage(level, type, result));
}
Перегрузите метод, сделав перегрузу без форматирования.
Например, так:
static void outMessage(LogFilter type, LogLevel level, string text)
{
Logger logger = GetLoggerByType(type);
logger.write(new LogMessage(level, type, text));
}
Если нет возможности отказаться от форматирования (скажем, оно закопано слишком глубоко) - можно принимать FormattableString
:
static void outMessage(LogFilter type, LogLevel level, FormattableString text)
{
outMessage(type, level, text.Format, text.GetArguments());
}