【问题标题】:C# compiling optimizations: null coalescing operator - UPDATED - Reflector's bug?C# 编译优化:空合并运算符 - 已更新 - 反射器的错误?
【发布时间】:2010-11-27 14:37:05
【问题描述】:

您好!我对 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");
    }
}

【问题讨论】:

  • 如果您使用 C# 4.0,我会考虑使用 Lazy 类进行延迟实例化。
  • 对我来说看起来很奇怪。几乎看起来像是反射器或 C# 中的错误。但在如此简单的代码中出现 C# 错误听起来不太可能。
  • 或许懂IL的人可以检查一下是不是reflector的bug
  • 这样的代码在虚拟控制台应用程序中正常工作,提供假设的输出。看起来像 Reflector 中的一个错误... Console.WriteLine(Host);主机=“覆盖”; Console.WriteLine(Host);
  • 您可以将其发布在反射器论坛中,以便他们确认是否是错误并修复它。

标签: c# compiler-construction reflector il


【解决方案1】:

这看起来像是 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.sdup 创建的值弹出堆栈,如果不是null,则跳转到L_0017
  • L_0008:pop 此时,_testnull,所以将该值从堆栈中弹出。

它继续以类似的方式评估_setting,如果_setting也是null,则最终返回"default"

现在,如果我们像这样在代码中添加一个赋值:

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_0017L_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# dissembly 并针对原来的运行它,你会看到它产生不同的结果。

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

【讨论】:

【解决方案2】:

我想我不明白 - 两个代码示例都是同义词。

请记住,Reflector 无法从编译器生成的 IL 中重现您的确切语法。有时语法会不同,但代码的语义和含义总是相同的。

【讨论】:

  • 我看不出这些是同义词。第一个调用(除了线程安全问题)正确的表达式仅一次,第二个在每次执行 getter 时调用它。
  • 是的,这实际上让我感到困惑。根本没有“延迟加载”。
  • @Callum Rogers:我看不出第二个有多懒惰。它每次都调用ConfigurationManager.AppSettings["Host"]。而第一个仅在host==null 时才调用它。而第二个中的if 代码根本没有任何作用。它分配给一个局部变量,但从不读取它。
猜你喜欢
  • 2012-08-13
  • 2010-12-13
  • 2012-10-05
  • 2019-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多