Pav*_*nin 29 c# string overloading type-inference string-interpolation
MSDN 文档包含有关隐式转换的部分:
var s = $"hello, {name}";
System.IFormattable s = $"Hello, {name}";
System.FormattableString s = $"Hello, {name}";
Run Code Online (Sandbox Code Playgroud)
从第一个字符串开始,原始类型的插值字符串就是string.好的,我可以理解它,但是......我意识到字符串没有实现IFormattable.所以它看起来像编译器的一些魔法类似于它对lambdas的作用.
现在猜猜这段代码的输出:
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);
}
Run Code Online (Sandbox Code Playgroud)
暗示:
我是System.String
我是System.Runtime.CompilerServices.FormattableStringFactory + ConcreteFormattableString
如果您从第二种方法中删除评论,您将获得:
我是一个字符串System.String
我是一个字符串System.String
好的
我可能不太了解重载分辨率,但是C#规范的 14.4.2 意味着首先定义传递参数的类型,但是lambdas如何工作呢?
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");
}
Run Code Online (Sandbox Code Playgroud)
删除评论和...
CS0121以下方法或属性之间的调用不明确:'UserQuery.PrintMe(Expression)'和'UserQuery.PrintMe(Action)'
所以我不明白编译器的行为.
更新:
为了使事情变得更糟,我已经检查了扩展方法的这种行为:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
现在我就是这样的:
我是System.String
我是System.Runtime.CompilerServices.FormattableStringFactory + ConcreteFormattableString
我是System.String
我是一个System.String
ang*_*son 22
新的插值字符串语法是部分编译器魔术和部分运行时类.
让我们浏览所有场景,看看实际发生了什么.
var s = $"{DateTime.Now}";
这被编译为:
string s = string.Format("{0}", DateTime.Now);
Run Code Online (Sandbox Code Playgroud)
有关详细信息,请参阅Try Roslyn.
string s = $"{DateTime.Now}";
这被编译为:
string s = string.Format("{0}", DateTime.Now);
Run Code Online (Sandbox Code Playgroud)
有关详细信息,请参阅Try Roslyn.
object s = $"{DateTime.Now}";
这被编译为:
object s = string.Format("{0}", DateTime.Now);
Run Code Online (Sandbox Code Playgroud)
有关详细信息,请参阅Try Roslyn.
IFormattable s = $"{DateTime.Now}";
这被编译为:
IFormattable s = FormattableStringFactory.Create("{0}", new object[] {
DateTime.Now
});
Run Code Online (Sandbox Code Playgroud)
有关详细信息,请参阅Try Roslyn.
FormattableString s = $"{DateTime.Now}";
这被编译为:
FormattableString s = FormattableStringFactory.Create("{0}", new object[] {
DateTime.Now
});
Run Code Online (Sandbox Code Playgroud)
有关详细信息,请参阅Try Roslyn.
所以我们可以总结一下编译器的魔力如下:
string,通过调用创建String.Format,然后这样做FormattableString,并创建一个viaFormattableStringFactory.Create由于我们还没有一份官方C#6标准文件,除了仔细阅读github存储库,问题和讨论之外,其具体规则尚不清楚(至少对我而言,请证明我错了!).
因此,上面的示例显示了如果编译器知道目标类型会发生什么,在这种情况下通过变量类型.如果我们调用一个没有重载的方法,那就是其中一种类型,就会发生完全相同的"魔法".
但是如果我们有超载会发生什么?
考虑这个例子:
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"); }
}
Run Code Online (Sandbox Code Playgroud)
执行此示例时,我们得到此输出:
string
Run Code Online (Sandbox Code Playgroud)
string即使有多种选择,显然仍然是首选.
有关详细信息,请参阅此.NET小提琴.
请注意,.NET Fiddle由于某种原因不允许我FormattableString直接使用,但如果我运行相同的代码,并且存在该重载,在LINQPad中,我仍然得到string输出.
如果我然后删除了string我得到的重载FormattableString,然后如果我删除了我得到的IFormattable,那么使用重载我可以观察规则是,并且在这里我们停止第一个重载:
stringFormattableStringIFormattableobject长话短说:
如果编译器找到PrintMe带string参数的方法,则会生成以下代码:
this.PrintMe("Hello World");
this.PrintMe(string.Format("{0}", "Hello World"));
Run Code Online (Sandbox Code Playgroud)
如果PrintMe使用string参数注释方法,则会生成以下代码:
this.PrintMe("Hello World");
this.PrintMe(FormattableStringFactory.Create("{0}", new object[] {"Hello World"}));
Run Code Online (Sandbox Code Playgroud)
然后,方法过载决定的部分我很容易猜测.
this.PrintMe("Hello World");选择object参数方法,因为"Hello World"不能隐式转换为IFormattable.
这是基于编译器的决定:
var s1 = $"{ "Hello World"}";
Run Code Online (Sandbox Code Playgroud)
生成(作为最佳选择):
string s1 = string.Format("{0}", "Hello World");
Run Code Online (Sandbox Code Playgroud)
和:
void PrintMe(IFormattable message)
{
Console.WriteLine("I am a " + message.GetType().FullName);
}
PrintMe($"{ "Hello World"}");
Run Code Online (Sandbox Code Playgroud)
生成(为了匹配方法签名):
this.PrintMe(FormattableStringFactory.Create("{0}", new object[] {"Hello World"}));
Run Code Online (Sandbox Code Playgroud)
对于扩展方法:
$"{"Hello World"}".PrintMe();
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);
}
}
Run Code Online (Sandbox Code Playgroud)
编译器$"{"Hello World"}"首先解析,这导致string作为最佳决策,然后检查是否PrintMe()找到了一个方法(由于字符串是一个,因此找到它object).所以生成的代码是:
string.Format("{0}", "Hello World").PrintMe();
Run Code Online (Sandbox Code Playgroud)
请注意,如果删除扩展方法object,则会出现编译时错误.