web-dev-qa-db-ja.com

コンパイラが「すべてのコードパスが値を返すわけではない」と文句を言うのはなぜですか?

コンパイラがこの関数に問題がある理由を理解しようとしています。 「すべてのコードパスが値を返すわけではありません」というエラーが表示されますが、aがtrueでない場合、制御フローがif( a )式に渡される状況はわかりません(したがって、if( a )は不要ですが、コンパイラはそれを認識していないようです)。

_public static Boolean Foo(Boolean x)
{
    Boolean a = false;
    if( x )
    {
        a = true;
    }
    else
    {
        try
        {
            SomethingThatMightThrow();
            Assert.IsFalse( a );
            return a;
        }
        catch(Exception)
        {
            a = true;
        }
    }

    if( a )
    {
        return x;
    }
}
_

即時の修正は、if( a )ガードステートメントを完全に削除し、すぐに_return x_だけにすることです-しかし、可能なすべてのコードパスがreturnにヒットすることを静的に証明できるはずなのに、コンパイラが文句を言うのはなぜですか?ステートメント?重要なのは、ループがないことです。これが、return- nessの証明に失敗する主な理由であることがよくあります。

VS2015 Update3を使用しています。

28
Dai

関数の最後に到達したときにafalseであるというシナリオがサポートされています。そのシナリオは、コードをデバッグし、デバッガーを使用してsetaからfalseに設定する場合です。

C#コンパイラのルールは設計上単純です。 C#が借用している言語では、関数が何も返さない可能性があるという問題は、コンパイラの警告ではカバーできない問題でした。誤検知が多すぎるため、警告が調整され、明らかなケースについてのみ警告し、誤検知が発生しました。 C#のルールは妥協案であり、ルールに精通している人間が理解できる場合は誤検知が許容され、誤検知は許容されません。関数に値を返さないコードパスがある場合、コンパイラがそれを検出することが保証されます。

これらの単純なルールの一部は、変数の値が考慮されないことです。 atrueであることが静的に保証されている場合でも、コンパイラーは設計上、その事実を利用できません。

@PetSerAlは、C#言語仕様の関連する文言をすでに引用しています。

8.1エンドポイントと到達可能性

[...]

特定のステートメントまたはエンドポイントが到達可能かどうかを判断するために、コンパイラーは、各ステートメントに定義された到達可能性ルールに従ってフロー分析を実行します。フロー分析では、ステートメントの動作を制御する定数式(§7.19)の値が考慮されますが、非定数式の可能な値は考慮されません。言い換えると、制御フロー分析の目的で、特定のタイプの非定数式は、そのタイプの可能な値を持っていると見なされます。

これは、現在公開されている最後の言語仕様の一部であり、C#5.0用のものです。使用しているバージョンのC#6.0(VS2015が提供するもの)にはまだ仕様が公開されていないため、表現が少し異なる可能性がありますが、コンパイラーが示しているように、事実上同じルールが適用されます。

52
user743382

実行時とコンパイル時

あなたの例はあまりにも複雑です。これもコンパイルされません:

static int Test()
{
    bool f = true;
    if (f)
    {
        return 1;
    }
    else
    {
        //Not all code paths return a value
    }
}

一方、これは次のようになります。

static int Test()
{
    if (true)
    {
        return 1;
    }
    else
    {
        //No error
    }
}

検証メカニズムが導入されていても、実行時変数の内容を推測するのに十分なロジックがないのではないかと思います。コンパイル時の変数は問題ありません。

26
John Wu

コンパイラーはコードに対して非常に単純な分析を行うため、戻り値を明示的に指定する必要があると思います。

これは間違った決定のように見えるかもしれませんが、複雑なコードを扱う場合、戻り値が明確でない場合があります。したがって、プログラマーはそれを返すことを余儀なくされます。

あなたの例はこのように最小限に減らすことができます:

public static Int32 Main(String[] args)
{
    var printUsage = true;
    if (printUsage)
    {
        return 0;
    }

    // return nothing, so compiler is not happy
}

まだエラーが発生している間。

注: Resharperを使用すると、必要な分析が実行され、それに応じて警告が表示されます。

if (printUsage)         // Warning: expression is always true 
6
Alexei