Вопрос C#: почему я не могу назначить строку непосредственно классу, наследующему System.IFormattable или System.FormattableString, хотя они сами могут?

      public class Program
{
    public void Main()
    {
        IFormattable string1 = $"{1},{"test"}";      //Works
        FormattableString string2 = $"{1},{"test"}"; //Works
        LocStr string3 = $"{1},{"test"}"; ///Error: Cannot implicitly convert type 'string' to 'LocStr'

    }
    public class LocStr : IFormattable
    {
        public string ToString(string format, IFormatProvider formatProvider)
        {
            throw new NotImplementedException();
        }
    }
}

Я пытаюсь создать свой собственный класс FormattableString, однако независимо от того, наследует ли мой класс от IFormattable или FormattableString, его нельзя напрямую назначить из форматированной строки, как вLocStr testStr= $"Hello {User.Name}";

Я пробовал наследовать от IFormattable, FormattableString, но ничего не получилось.

Я проверил, могу ли я переопределить оператор присваивания =, но, по-видимому, это не разрешено в С#, как вpublic static LocStr operator =(string a)но это не разрешено.

1 ответ

За кулисами творится какая-то магия компилятора. Если вы определяете тип переменной явно или разрешаете компилятору определять его с помощьюvarтогда это приведет к типуstring:

      var string0 = $"{1},{"test"}";
Console.WriteLine(string0.GetType()); // System.String

Но для форматируемых строк существует также специальный тип:

Экземпляр может быть результатом интерполированной строки в C# или Visual Basic.

который вы действительно можете использовать в своем коде, что приводит к следующему поведению:

      IFormattable string1 = $"{1},{"test"}";      //Works
FormattableString string2 = $"{1},{"test"}"; //Works

Console.WriteLine(string1.GetType()); // System.Runtime.CompilerServices.FormattableStringFactory+ConcreteFormattableString
Console.WriteLine(string2.GetType()); // System.Runtime.CompilerServices.FormattableStringFactory+ConcreteFormattableString

Демо @sharplab.io (также ознакомьтесь с декомпиляцией в C#).

На самом деле это описано в разделе «Неявные преобразования» и «Как указать». IFormatProviderраздел реализации документов:

Существует три неявных преобразования интерполированной строки:

  1. Преобразование интерполированной строки вStringпример. ...
  2. Преобразование интерполированной строки вFormattableStringэкземпляр, представляющий строку составного формата вместе с результатами выражения, подлежащими форматированию. ...
  3. Преобразование интерполированной строки в экземпляр, который также позволяет создавать несколько строк результатов с содержимым, зависящим от культуры, из однойIFormattableпример. ...

Компилятор «из коробки» не знает, как преобразовать интерполированную строку в ваш конкретный тип, но начиная с C# 10 вы можете реализовать шаблон обработчика интерполированной строки (хотя в общем случае это не обязательно, обычно это необходимо для конкретных сценариев высокой производительности):

      [InterpolatedStringHandler]
public class LocStr : IFormattable
{
    public LocStr(int literalLength, int formattedCount)
    {
        
    }
    public string ToString(string format, IFormatProvider formatProvider)
    {
        throw new NotImplementedException();
    }
    
    public void AppendLiteral(string s)
    {
    }

    public void AppendFormatted<T>(T t)
    {
    }
}

Обновленное демо @sharplab.io

Другие вопросы по тегам