您好!我对 C# 编译器如何执行优化有点困惑。
我编写了以下 getter 来弥补“惰性”初始化,以及 null 情况下的默认值:
静态类助手:
private static string host;
public static string Host
{
get
{
return host ?? (host= (ConfigurationManager.AppSettings["Host"] ?? "host.ru"));
}
}
这是Reflector拆解的结果:
public static string Host
{
get
{
if (Helper.host == null)
{
string host = Helper.host;
}
return (Helper.host = ConfigurationManager.AppSettings["Host"] ?? "host.ru");
}
}
看起来它会以与假设不同的方式工作......
更新
private static string host;
public static string Host
{
get
{
return host ?? (host = (GetVal() ?? "default"));
}
}
static void Main(string[] args)
{
Console.WriteLine(Host);
host = "overwritten";
Console.WriteLine(Host);
}
static string GetVal()
{
return "From config";
}
工作正常(来自配置,被覆盖),但 Reflector 显示相同:
public static string Host
{
get
{
if (Program.host == null)
{
string host = Program.host;
}
return (Program.host = GetVal() ?? "default");
}
}
这看起来像是 Reflector 的 C# 反汇编中的错误。
从这段代码开始:
public static string _test;
public static string _setting;
public static string Test_1
{
get { return _test ?? (_setting ?? "default"); }
}
Reflector 显示了此 C# 反汇编:
public static string Test_1
{
get
{
return (_test ?? (_setting ?? "default"));
}
}
以及对应的IL:
.method public hidebysig specialname static string get_Test_1() cil managed
{
.maxstack 8
L_0000: ldsfld string ConsoleApplication1.Program::_test
L_0005: dup
L_0006: brtrue.s L_0017
L_0008: pop
L_0009: ldsfld string ConsoleApplication1.Program::_setting
L_000e: dup
L_000f: brtrue.s L_0017
L_0011: pop
L_0012: ldstr "default"
L_0017: ret
}
我不是 IL 专家,但这是我的看法:
L_0000:
ldsfld
将 _test
推入计算堆栈L_0005:
dup
复制计算堆栈最顶层的值(_test
)并将其推入堆栈。L_0006:
brtrue.s
将 dup
创建的值从堆栈中弹出,如果不是 L_0017
,则跳转到 null
。L_0008:
pop
此时,_test
是null
,所以从堆栈中弹出该值。并且它继续以类似的方式评估
_setting
,如果 "default"
也是 _setting
,则最终返回 null
。
现在,如果我们在代码中添加一个赋值,如下所示:
public static string Test_2
{
get { return _test ?? (_test = (_setting ?? "default")); }
}
Reflector 显示了此 C# 反汇编:
public static string Test_2
{
get
{
if (_test == null)
{
string text1 = _test;
}
return (_test = _setting ?? "default");
}
}
这是不正确的(如果
_test
不是 null
,则不会返回 _test
,而是将 _setting
或 "default"
分配给 _test
,然后返回)。
但是,IL 反汇编看起来像
Test_1
的 IL,在 L_0017
和 L_0018
处有一些额外的指令来完成分配。
.method public hidebysig specialname static string get_Test_2() cil managed
{
.maxstack 8
L_0000: ldsfld string ConsoleApplication1.Program::_test
L_0005: dup
L_0006: brtrue.s L_001d
L_0008: pop
L_0009: ldsfld string ConsoleApplication1.Program::_setting
L_000e: dup
L_000f: brtrue.s L_0017
L_0011: pop
L_0012: ldstr "default"
L_0017: dup
L_0018: stsfld string ConsoleApplication1.Program::_test
L_001d: ret
}
最后,如果您复制 Reflector 的 C# 反汇编并针对原始版本运行它,您会发现它会产生不同的结果。
using System;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
_test = "Test";
Console.WriteLine(Test_2);
Console.WriteLine(Reflector_Test_2);
Console.ReadLine();
}
public static string _test;
public static string _setting;
public static string Test_1
{
get { return _test ?? (_setting ?? "default"); }
}
public static string Test_2
{
get { return _test ?? (_test = (_setting ?? "default")); }
}
public static string Reflector_Test_2
{
get
{
if (_test == null)
{
string text1 = _test;
}
return (_test = _setting ?? "default");
}
}
}
}
输出
Test
default
我想我不明白 - 两个代码示例是同义词。
请记住,Reflector 无法从编译器生成的 IL 中重现您的确切语法。 有时语法会有所不同,但代码的语义和含义始终是相同的。