Greetings! I am slightly confused about how does C# compiler perform its optimizations.
I have written the following getter to make up "lazy" initialization, and default value in case of null:Static class Helper:
private static string host;
public static string Host
{
get
{
return host ?? (host= (ConfigurationManager.AppSettings["Host"] ?? "host.ru"));
}
}
Here is the result of disassembling by Reflector:
public static string Host
{
get
{
if (Helper.开发者_运维问答host == null)
{
string host = Helper.host;
}
return (Helper.host = ConfigurationManager.AppSettings["Host"] ?? "host.ru");
}
}
Looks like it would work in other way than assumed...
UPDATE
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";
}
Works correctly (From config, overwritten), but Reflector shows the same:
public static string Host
{
get
{
if (Program.host == null)
{
string host = Program.host;
}
return (Program.host = GetVal() ?? "default");
}
}
This looks like a bug in Reflector's C# disassembly.
Starting with this code:
public static string _test;
public static string _setting;
public static string Test_1
{
get { return _test ?? (_setting ?? "default"); }
}
Reflector shows this C# disassembly:
public static string Test_1
{
get
{
return (_test ?? (_setting ?? "default"));
}
}
and the corresponding 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
}
I am not an IL expert, but this is my take on it:
L_0000:
ldsfld
pushes_test
onto the evaluation stackL_0005:
dup
copies the value (_test
) that is topmost on the evaluation stack and pushes that onto the stack.L_0006:
brtrue.s
pops the value created bydup
off the stack and jumps toL_0017
if it is notnull
.L_0008:
pop
at this point,_test
isnull
, so pop that value off the stack.
and it continues to evaluate _setting
in a similar fashion, finally returning "default"
if _setting
is also null
.
Now, if we add an assignment into the code like this:
public static string Test_2
{
get { return _test ?? (_test = (_setting ?? "default")); }
}
Reflector shows this C# disassembly:
public static string Test_2
{
get
{
if (_test == null)
{
string text1 = _test;
}
return (_test = _setting ?? "default");
}
}
which is not correct (if _test
is not null
, instead of returning _test
, it assigns _setting
or "default"
to _test
and then returns).
However, the IL dissassembly looks like the IL for Test_1
, with a couple of extra instructions at L_0017
and L_0018
to do the assignment.
.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
}
Finally, if you copy Reflector's C# dissembly and run it against the original, you'll see it produces different results.
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");
}
}
}
}
Outputs
Test
default
I guess I don't understand - both code examples are synonymous.
Remember that Reflector cannot reproduce your exact syntax from the IL that the compiler generates. Sometimes the syntax will be different but the semantics and meaning of the code will always be the same.
精彩评论