web-dev-qa-db-ja.com

このコードに到達できないのはなぜですか?

到達不能と思われるコードがあり、検出されない場合がありました。コンパイラもVisual Studioも警告を発行しません。

次のコードを検討してください。

enum Foo { A, B, C }
class Bar { public Foo type; }

static class Program
{
    private static void Main()
    {
        var bar = new Bar { type = Foo.A };

        if (bar.type == Foo.B)
        {
            Console.WriteLine("lol");
        }
    }
}

明らかに、ifステートメントの条件がfalseであるため、プログラムは「lol」を出力しません。ただし、到達不能なコードに対して警告が発行されない理由はわかりません。私の唯一の仮説は、マルチスレッドプログラムで競合状態がある場合に到達できる可能性があるということです。これは正しいです?

49

静的分析は多くのことしかできず、値を変更できないことを証明できる場合にのみ、コードを到達不能としてマークします。コードでは、Barの内部で何が起こるかはメソッドフローの範囲外であり、静的に推論することはできません。 Barのコンストラクターが、typeの値をBに戻すスレッドを起動した場合はどうなりますか?コンパイラーは、Barの内部がメソッドにスコープされていないため、それを知ることができません。

コードがlocal変数の値をチェックしていた場合、コンパイラは変更する方法がないかどうかを知ることができました。しかし、ここではそうではありません。

134

C#仕様 は、

Ifステートメントの最初の埋め込みステートメントは、ifステートメントが到達可能であり、ブール式に定数値falseがない場合に到達可能です。

そして、 定数式 について

定数式は、nullリテラル、またはsbyte、byte、short、ushort、int、uint、long、ulong、char、float、double、decimal、bool、object、string、anyのいずれかのタイプの値である必要があります列挙型。

定数式では次の構成体のみが許可されます。

  • リテラル(nullリテラルを含む)。
  • クラスおよび構造体型のconstメンバーへの参照。
  • 列挙型のメンバーへの参照。
  • Constパラメーターまたはローカル変数への参照
  • 括弧で囲まれた部分式。それ自体が定数式です。
  • ターゲットタイプが上記のタイプのいずれかである場合、キャスト式。チェックされた式とチェックされていない式
  • デフォルト値式
  • 定義済みの+!、および~単項演算子。
  • 定義済みの+*/%<<>>&、 |、^&&||==!=<><=、および>=二項演算子。各オペランドが上記のタイプである場合。
  • ?:条件演算子。

メンバーアクセス式はこのリストにないため、ブール式は定数ではありません。したがって、ifブロックの本体に到達できます。

27
Rawling

コンパイル時にそのような保証はできないためです。この代替Barクラスを検討してください

class Bar
{
   Random random = new Random();
   Array Foos = Enum.GetValues(typeof(Foo));

    private Foo _type;
    public Foo type
    {
        get { return _type; }
        set
        {
            _type = (Foo)Foos.GetValue(random.Next(3));
        }
    }
}

「到達可能」は機能レベルで定義されることに注意してください。安全であっても、テストされている関数の外部に到達することは許可されていません。

9

期待する警告は、有用な警告ではないため実装されていません。

実世界のアプリケーションでは、コンパイラは非常に頻繁に到達不能であることを完全に証明できるコードに直面しています。

static class Program
{
    private static void Main()
    {
        if (false)
        {
            Console.WriteLine("lol");
        }
    }
}

このコンピューターにはC#コンパイラーはありませんが、警告もありません。これは、コードブロックの周りにif (false) { ... }を配置するときに、意図的に、おそらく実験のために一時的に何かを無効にするために行ったためです。それについてしつこく言っても役に立たないでしょう。

より一般的なのは、リテラルfalseではなく、ビルドシステムが構成に応じてtrueまたはfalseに設定するコンパイル時定数です。あるビルドでは到達不能なコードをコンパイラーで削除し、別のビルドでは削除しないようにし、どちらの方法でも苦情を望まないようにします。

それよりもさらに一般的なのは、インライン化やdiscoverへの定数伝播などの初期の最適化です。条件は常にfalseです。あなたのようなものがあると仮定します

static class Program
{
    private static void Fizz(int i)
    {
        if (i % 3 == 0) {
            Console.WriteLine("fizz");
        } else {
            Console.WriteLine(i);
        }
    }

    private static void Main()
    {
        Fizz(4);
    }
}

Fizz()内の条件の片側が、引数4 (このプログラム)でしか呼び出されなかったという理由だけで到達不能であったことを、明らかに伝えたくないでしょう。

2
zwol