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
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.
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.
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.
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.
var s = $"{DateTime.Now}";
This gets compiled as this:
string s = string.Format("{0}", DateTime.Now);
See Try Roslyn for details.
string s = $"{DateTime.Now}";
This gets compiled as this:
string s = string.Format("{0}", DateTime.Now);
See Try Roslyn for details.
object s = $"{DateTime.Now}";
This gets compiled as this:
object s = string.Format("{0}", DateTime.Now);
See Try Roslyn for details.
IFormattable s = $"{DateTime.Now}";
This gets compiled as this:
IFormattable s = FormattableStringFactory.Create("{0}", new object[] { DateTime.Now });
See Try Roslyn for details.
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:
string
, created with a call to String.Format
, then do thatFormattableString
, 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:
string
FormattableString
IFormattable
object
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With