Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the original type of interpolated string?

MSDN docs contain the section about implicit conversions:

var s = $"hello, {name}"; System.IFormattable s = $"Hello, {name}"; System.FormattableString s = $"Hello, {name}"; 

From the first string it follows that original type of interpolated string is string. Ok, I can understand it, but then… I realize that string does not implement IFormattable. So it looks like some magic from the compiler similar to what it does with lambdas.

Now guess the output of this code:

void Main() {     PrintMe("Hello World");     PrintMe($"{ "Hello World"}"); }  void PrintMe(object message) {     Console.WriteLine("I am a " + message.GetType().FullName); }  //void PrintMe(string message) //{ //  Console.WriteLine("I am a string " + message.GetType().FullName); //}  void PrintMe(IFormattable message) {     Console.WriteLine("I am a " + message.GetType().FullName); } 

Hint:

I am a System.String
I am a System.Runtime.CompilerServices.FormattableStringFactory+ConcreteFormattableString

If you remove comments from the second method you'll get:

I am a string System.String
I am a string System.String

Ok
May be I do not understand well overloading resolution, but 14.4.2 of C# spec implies that the type of the passed parameter is defined first, but again how do then lambdas work?

void Main() {     PrintMe(() => {});     PrintMe(() => {}); }  void PrintMe(object doIt) {     Console.WriteLine("I am an object"); }  //void PrintMe(Expression<Action> doIt) //{ //  Console.WriteLine("I am an Expression"); //}  void PrintMe(Action doIt) {     Console.WriteLine("I am a Delegate"); } 

Remove comments and...

CS0121 The call is ambiguous between the following methods or properties: 'UserQuery.PrintMe(Expression)' and 'UserQuery.PrintMe(Action)'

So I do not understand compiler's behavior here.

Update:

To make things worse I've checked this behavior for extension methods:

void Main() {     PrintMe("Hello World");     PrintMe($"{"Hello World"}");      "Hello World".PrintMe();     $"{"Hello World"}".PrintMe(); }  void PrintMe(object message) {     Console.WriteLine("I am a " + message.GetType().FullName); }  void PrintMe(IFormattable message) {     Console.WriteLine("I am a " + message.GetType().FullName); }  public static class Extensions {     public static void PrintMe(this object message)     {         Console.WriteLine("I am a " + message.GetType().FullName);     }      public static void PrintMe(this IFormattable message)     {         Console.WriteLine("I am a " + message.GetType().FullName);     } } 

Now I have it like that:

I am a System.String
I am a System.Runtime.CompilerServices.FormattableStringFactory+ConcreteFormattableString
I am a System.String
I am a System.String

like image 454
Pavel Voronin Avatar asked Jun 30 '16 09:06

Pavel Voronin


People also ask

What is interpolation string?

In computer programming, string interpolation (or variable interpolation, variable substitution, or variable expansion) is the process of evaluating a string literal containing one or more placeholders, yielding a result in which the placeholders are replaced with their corresponding values.

What is an interpolated string in C#?

An interpolated string is a string literal that might contain interpolation expressions. When an interpolated string is resolved to a result string, items with interpolation expressions are replaced by the string representations of the expression results. This feature is available starting with C# 6.

What is the correct syntax for string interpolation?

Syntax of string interpolation starts with a '$' symbol and expressions are defined within a bracket {} using the following syntax. Where: interpolatedExpression - The expression that produces a result to be formatted.


1 Answers

The new interpolated string syntax is part compiler magic and part runtime classes.

Let's go through all the scenarios and see what is actually happening.

  1. var s = $"{DateTime.Now}";

    This gets compiled as this:

    string s = string.Format("{0}", DateTime.Now); 

    See Try Roslyn for details.

  2. string s = $"{DateTime.Now}";

    This gets compiled as this:

    string s = string.Format("{0}", DateTime.Now); 

    See Try Roslyn for details.

  3. object s = $"{DateTime.Now}";

    This gets compiled as this:

    object s = string.Format("{0}", DateTime.Now); 

    See Try Roslyn for details.

  4. IFormattable s = $"{DateTime.Now}";

    This gets compiled as this:

    IFormattable s = FormattableStringFactory.Create("{0}", new object[] {     DateTime.Now }); 

    See Try Roslyn for details.

  5. FormattableString s = $"{DateTime.Now}";

    This gets compiled as this:

    FormattableString s = FormattableStringFactory.Create("{0}", new object[] {     DateTime.Now }); 

    See Try Roslyn for details.

So we can summarize the compiler magic as follows:

  1. If we can get by with just using string, created with a call to String.Format, then do that
  2. If not, use FormattableString, and create one via FormattableStringFactory.Create

Since we do not yet have an officiel C# 6 standards document, other than perusing the github repositories, issues, and discussions, the exact rules for this is not known (at least not to me, please prove me wrong!).

So, the above examples shows what happens if the compiler knows the target type, in this case through the variable type. If we call a single method, with no overloads, that has one of those types, the exact same "magic" will happen.

But what happens if we have overloads?

Consider this example:

using System;  public class Program {     public static void Main()     {         Test($"{DateTime.Now}");     }      public static void Test(object o) { Console.WriteLine("object"); }     public static void Test(string o) { Console.WriteLine("string"); }     public static void Test(IFormattable o) { Console.WriteLine("IFormattable"); }     // public static void Test(FormattableString o) { Console.WriteLine("FormattableString"); } } 

When executing this example we get this output:

string 

So clearly string is still preferred, even when multiple options are available.

See this .NET fiddle for details.

Note that .NET Fiddle for some reason does not allow me to use FormattableString directly, but if I run the same code, with that overload present, in LINQPad, I still get string as the output.

If I then remove the string overload I get FormattableString, and then if I remove that I get IFormattable, so with overloads I can then observe that the rules are, and here we stop with the first overload that has:

  1. string
  2. FormattableString
  3. IFormattable
  4. object
like image 146
Lasse V. Karlsen Avatar answered Sep 28 '22 20:09

Lasse V. Karlsen