2

こんにちは!C#コンパイラが最適化をどのように実行するかについて少し混乱しています。
「怠惰な」初期化とnullの場合のデフォルト値を構成するために次のゲッターを作成しました。

静的クラスヘルパー:

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

想定以外の方法で動作するようです...

アップデート

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

2 に答える 2

1

これは、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_0017null
  • L_0008:popこの時点で、_testnullなので、その値をスタックからポップします。

_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_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#アセンブリをコピーして元のアセンブリに対して実行すると、異なる結果が生成されることがわかります。

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
于 2010-11-27T18:48:36.310 に答える
0

私は理解していないと思います-両方のコード例は同義です。

Reflectorは、コンパイラが生成するILから正確な構文を再現できないことに注意してください。構文が異なる場合もありますが、コードのセマンティクスと意味は常に同じです。

于 2010-11-27T14:39:26.180 に答える