2
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();
        }
    }
}

I am trying to create my own custon FormattableString class, however whether my class inherits from IFormattable or FormattableString, it cannot be directly be assigned from a formated string, as in LocStr testStr= $"Hello {User.Name}";

I tried inheriting from IFormattable, FormattableString, neither worked.

I checked if I can override assignment operator = but it is apparently not allowed in C# as in public static LocStr operator =(string a) but it is not allowed.

  • 1
    If you don't want to write an `InterpolatedStringHandler` you should be able to write an implicit cast operator from `IFormattable`. – Jeremy Lakeman Jul 19 '23 at 01:18
  • That is a good suggestion however, unfortunately, implicit casting is not allowed from FormattableString (Error: "user defined conversions to and from a base type are not allowed") or an interface ("Error: "user defined conversions to and from an interface are not allowed") I got these errors after trying to define implicit casting as such; public static implicit operator locStr(FormattableString a) { locStr b = new locStr(); b.format = a.Format; b.arguments = a.GetArguments(); return b; } – osman doluca Jul 19 '23 at 07:26
  • unfortunately even if I define an implicit conversion to formattableString, the compiler doesn't recognise it and requests an explicit casting. After a couple days, I cudnt find a solution to do this using C# version 9. – osman doluca Jul 20 '23 at 21:15
  • 1
    Ah, right. Because there is no dotnet type for an "interpolated_string_expression", you can't define more implicit casts for it. The compiler will only implicitly convert it to `FormattableString` / `IFormattable`, otherwise it's a `string`. https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/conversions#1025-implicit-interpolated-string-conversions. The usual search order for implicit conversions don't apply, so you can't chain them `$"" => FormattableString => LocStr`. Sigh. – Jeremy Lakeman Jul 21 '23 at 00:51

1 Answers1

2

There is some compiler magic going behind the scenes. If you define variable type as explicitly string or allow compiler to determine it with var then it will result in type string:

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

But for formattable strings there is also a special type FormattableString:

A FormattableString instance may result from an interpolated string in C# or Visual Basic.

which you can actually use in your code which results in the following behavior:

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

Demo @sharplab.io (also check out the decompilation to C#).

This is actually described in Implicit conversions and how to specify IFormatProvider implementation section of the docs:

There are three implicit conversions from an interpolated string:

  1. Conversion of an interpolated string to a String instance. ...
  2. Conversion of an interpolated string to a FormattableString instance that represents a composite format string along with the expression results to be formatted. ...
  3. Conversion of an interpolated string to an IFormattable instance that also allows you to create multiple result strings with culture-specific content from a single IFormattable instance. ...

Compiler out of the box does not know how to convert the interpolated string into your specific type but since C# 10 you can implement the interpolated string handler pattern (not that you should though in general case, usually it is needed for specific high performance scenarios):

[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)
    {
    }
}

Updated demo @sharplab.io

Guru Stron
  • 102,774
  • 10
  • 95
  • 132
  • Thank you so much for your answer. It worked perfectly on C# 10. But is there any way to do this without InterploatedStringHandler attribute? (I am using C# 9) I tried implicit cast operator as suggested by @Jeremy but did not work as it turns out implicit casting from an interface (IFormattable) or a base class (FormattableString) are not allowed. – osman doluca Jul 19 '23 at 07:28
  • @osmandoluca if you are using newer compiler you can try adding the attribute with the same name to your C# 9 project, other than that - no AFAIK. – Guru Stron Jul 19 '23 at 07:46