C# 将此.NET字符串更改为常量是否会提高性能?IL会自动做到这一点吗?
给定以下简单的.NET代码,就字符串C# 将此.NET字符串更改为常量是否会提高性能?IL会自动做到这一点吗?,c#,.net,C#,.net,给定以下简单的.NET代码,就字符串“xml”而言,这两者之间有什么区别吗 vs 请注意,“xml”这个词在课堂上再也不用了。就在这一点上 我的印象是,较新版本的.NET编译器将这样的简单字符串转换为常量(隐藏) 更可怕的是,如果在另一个地方使用了另一个“xml”字符串,编译器将只创建一个常量,并且这两个引用都将引用该单个字符串引用/内存分配 那么,在幕后的表现有什么不同吗 免责声明:请不要发表“微优化”评论。我明白了。这实际上是关于理解事物而不是试图微优化一行代码。对于编译器来说也是如此。基本
“xml”
而言,这两者之间有什么区别吗
vs
请注意,“xml”
这个词在课堂上再也不用了。就在这一点上
我的印象是,较新版本的.NET编译器将这样的简单字符串转换为常量(隐藏)
更可怕的是,如果在另一个地方使用了另一个“xml”
字符串,编译器将只创建一个常量,并且这两个引用都将引用该单个字符串引用/内存分配
那么,在幕后的表现有什么不同吗
免责声明:请不要发表“微优化”评论。我明白了。这实际上是关于理解事物而不是试图微优化一行代码。对于编译器来说也是如此。基本上,这两个变量在运行时都无法更改 这是一个例子:
public static void Main()
{
Console.WriteLine("Hello World");
const string hello = "Hello World";
Console.WriteLine(hello);
}
汇编至:
.method public hidebysig static void Main() cil managed
{
//
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Hello World"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ldstr "Hello World"
IL_0011: call void [mscorlib]System.Console::WriteLine(string)
IL_0016: nop
IL_0017: ret
}
正如你所看到的,它们完全一样。编译器使用ldstr
为文本字符串推送字符串对象
您可以将
常量字符串
移到方法之外,这不会有什么区别,因为文本被视为常量。您的问题的答案是:不,您不会看到性能提高
C#规范在第2.4.4.5节字符串文本中明确说明了相同文本字符串的情况:
每个字符串文本不一定会产生一个新的字符串实例。当两个或多个根据字符串相等运算符(§7.10.7)等效的字符串文字出现在同一程序中时,这些字符串文字引用同一字符串实例
此外,第7.19节常量表达式规定:
[常量表达式]在编译时计算。
常量表达式的编译时求值使用与非常量表达式的运行时求值相同的规则,但运行时求值会引发异常的情况除外,编译时求值会导致发生编译时错误
这表明const和literal字符串的处理方式相同
如果您检查编译器的IL输出,您会发现常量字符串产生的输出与使用文本字符串产生的输出相同
更可怕的是,如果在另一个地方使用了另一个“xml”字符串,编译器将只创建一个常量,而这两个引用都将引用该单个字符串引用/内存分配
这是正常的行为,因为.NET默认设置为“字符串插入”,所以许多变量将引用相同的内存分配
因此.NET会自动为所有字符串文本执行字符串实习。这是通过一个实习生池来完成的,实习生池是一个存储对所有唯一字符串的引用的特殊表
编译阶段上只保留显式声明的字符串文本。不会检查运行时创建的字符串是否已添加到池中。例如:
实习例子
string s="AB"; //will be interned
string s1 ="C";
string s2 = s1+s2 //will not be interned
对于您的第一个问题,没有区别,因为在编译时,编译器可以做出明智的决定,用内容替换变量,因为const是硬编码的,这会提高性能
因此,您不会看到任何性能提高。由于文本字符串通常由编译器托管,因此没有任何差异 以下所有示例都将打印出
true
:
- 例1:
const string constHello = "Hello"; void Foo() { var localHello = "Hello"; Console.WriteLine(ReferenceEquals(constHello, localHello)); }
- 例2:
string fieldHello = "Hello"; void Foo() { var localHello= "Hello"; Console.WriteLine(ReferenceEquals(fieldHello, localHello)); }
- 例3:
const string constHello = "Hello"; void Foo() { Console.WriteLine({IsReferenceEquals("Hello")}"); } public static bool IsReferenceEquals(string s) => ReferenceEquals(constHello, s);
在所有情况下,
“Hello”
只有一个实例,这可能对您有所帮助:“当编译器在C#源代码中遇到常量标识符时,它会将文本值直接替换到它生成的中间语言(IL)代码中”当您从另一个程序集引用公共常量时,这可能会导致问题,然后,使用不同的常量值重新编译该程序集。因为值是内联的,引用它的代码永远看不到更新。请阅读并分享您的研究。一个非常相关的术语是“字符串实习”,你一定在研究过程中遇到过,但你的问题中没有出现。没有性能差异,至于为什么要在方法内部使用const
,请参见。太长,读不下去了您不能在方法中使用readonly
,而const
只是询问编译器“请告诉我我是否愚蠢,是否会尝试修改此变量”的一种方式。只需使用反编译器,即使是ildasm.exe也可以。你会发现没有什么区别,C编译器使const
细节消失,并生成完全相同的IL。在使用常量有意义的地方使用const
。
string fieldHello = "Hello";
void Foo()
{
var localHello= "Hello";
Console.WriteLine(ReferenceEquals(fieldHello, localHello));
}
const string constHello = "Hello";
void Foo()
{
Console.WriteLine({IsReferenceEquals("Hello")}");
}
public static bool IsReferenceEquals(string s) => ReferenceEquals(constHello, s);