[c#6] практические примеры string interpolation
От: Sinix  
Дата: 27.02.15 08:05
Оценка: 59 (9)
Я точно помню, что 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/логирование, но и сценариев выше уже достаточно. Будет интересно
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.