Я точно помню, что string interpolation + FormattableString уже обсуждались, но поскольку поиск ушёл в несознанку, ссылок на прошлые обсуждения не будет
Собственно,
теория:
using System.Console;
var to = "world";
CallString($"Hello, {to}!");
CallFormattableString($"Hello, {to}!");
...
void CallString(string message)
{
WriteLine(message); // Hello, world!
}
void CallFormattableString(FormattableString message)
{
WriteLine(message.ToString()); // Hello, world!
WriteLine(message.Format); // Hello, {0}!
WriteLine(message.GetArguments()[0]); // world
}
А вот что с этим
можно сделать (спасибо гуглу за ссылку, Thomas Levesque — за готовый код
). Нужна vs2015 ctp6:
1. Url escaping:
class UrlFormatProvider : IFormatProvider
{
private readonly UrlFormatter _formatter = new UrlFormatter();
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return _formatter;
return null;
}
class UrlFormatter : ICustomFormatter
{
public string Format(string format, object arg, IFormatProvider formatProvider)
{
if (arg == null)
return string.Empty;
if (format == "r")
return arg.ToString();
return Uri.EscapeDataString(arg.ToString());
}
}
}
...
static string Url(FormattableString formattable)
{
return formattable.ToString(new UrlFormatProvider());
}
...
string url = Url($"http://foobar/item/{id}/{name}");
2. Sql:
class SqlFormatProvider : IFormatProvider
{
private readonly SqlFormatter _formatter = new SqlFormatter();
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return _formatter;
return null;
}
class SqlFormatter : ICustomFormatter
{
public string Format(string format, object arg, IFormatProvider formatProvider)
{
if (arg == null)
return "NULL";
if (arg is string)
return "'" + ((string)arg).Replace("'", "''") + "'";
if (arg is DateTime)
return "'" + ((DateTime)arg).ToString("MM/dd/yyyy") + "'";
if (arg is IFormattable)
return ((IFormattable)arg).ToString(format, CultureInfo.InvariantCulture);
return arg.ToString();
}
}
}
...
static string Sql(FormattableString formattable)
{
return formattable.ToString(new SqlFormatProvider());
}
...
string sql = Sql($"insert into items(id, name, creationDate) values({id}, {name}, {DateTime.Now})");
тут автор немного недодумал, по уму надо в SqlFormatProvider заполнять коллекцию параметров, в запрос подставлять их имена
(например, для ms sql вместо arg.ToString() возвращать "@Value1", "@Value2" etc).
Ну и возвращать SqlCommand, а не строку.
3. Ресурсы:
Самое интересное — работу с ресурсами — автор не рассмотрел, но в принципе ничего невозможного нет, заводим ресурс с именем "Hello_0",
var x = Properties.Resources.Format("Hello {txt}");
, дальше по аналогии.
Самое сложное — добавить roslyn diagnostics, чтобы проверять опечатки в именах ресурсов.
Саммари:
Код работает с любыми методами, принимающими FormattableString, причём FromattableString в теории можно заменить своим типом, см обсуждения:
раз,
два.
Впрочем, до релиза поведение может кучу раз поменяться, закладываться не стоит.
В принципе можно натянуть тот же подход на html/xml/логирование, но и сценариев выше уже достаточно. Будет интересно