Оптимизация компиляции в C #: оператор объединения нулей - ОБНОВЛЕНО - Ошибка рефлектора? - PullRequest
2 голосов
/ 27 ноября 2010

Привет! Я немного озадачен тем, как компилятор C # выполняет свои оптимизации.
Я написал следующий метод получения, чтобы выполнить "ленивую" инициализацию, и значение по умолчанию в случае нулевого значения:

Статический класс Помощник:

private static string host;  
public static string Host  
{        
    get  
    {  
        return host ?? (host= (ConfigurationManager.AppSettings["Host"] ?? "host.ru"));  
    }  
}

Вот результат разборки рефлектором:

public static string Host 
{  
    get  
    {  
        if (Helper.host == null)  
        {  
            string host = Helper.host;  
        }  
        return (Helper.host = ConfigurationManager.AppSettings["Host"] ?? "host.ru");  
    }  
}

Похоже, это будет работать не так, как предполагалось ...

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";
    }

Работает правильно (из конфигурации, перезаписано), но Reflector показывает то же самое:

public static string Host
{
    get
    {
        if (Program.host == null)
        {
            string host = Program.host;
        }
        return (Program.host = GetVal() ?? "default");
    }
}

Ответы [ 2 ]

1 голос
/ 27 ноября 2010

Это похоже на ошибку в разборке Reflector C #.

Начиная с этого кода:

public static string _test;
public static string _setting;

public static string Test_1
{
    get { return _test ?? (_setting ?? "default"); }
}

Отражатель показывает эту разборку 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")); }
}

Отражатель показывает эту разборку C #:

public static string Test_2
{
    get
    {
        if (_test == null)
        {
            string text1 = _test;
        }
        return (_test = _setting ?? "default");
    }
}

, что неверно (если _test не null, вместо возврата _test он присваивает _setting или "default" _test и затем возвращает).

Однако, разборка IL выглядит как IL для Test_1, с парой дополнительных инструкций в 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
0 голосов
/ 27 ноября 2010

Полагаю, я не понимаю - оба примера кода являются синонимами.

Помните, что Reflector не может воспроизвести ваш точный синтаксис из IL, который генерирует компилятор.Иногда синтаксис будет другим, но семантика и смысл кода всегда будут одинаковыми.

...