1

私は次の(簡略化された)方法を持っています:

public bool DoWorkWithRetry()
{
    for (int remainingTries = Constants.MaxRetries; remainingTries >= 0; remainingTries--)
    {
        try
        {
            return DoWork();
        }
        catch (Exception ex)
        {
            if (remainingTries == 0)
            {
                throw new WorkException(
                        String.Format("Failed after {0} retries.", Constants.MaxRetries),
                        ex);
            }
            // fall through to retry
        }
    }
}

このメソッドが を返すスローするかは明らかです。ただし、C# コンパイラは私に不平を言いnot all code paths return a valueます。

  • これは C# コンパイラのコード分析の制限ですか?
  • forまたは、スローまたはリターンなしでループが完了できる場所が表示されないという条件がありますか?
4

5 に答える 5

12

コンパイラは言語仕様に従っているだけです。

この言語は、「整数から始めて 1 を引くことを繰り返すと、必ずある時点でゼロに到達する」という分析を実行しようとしません。それConstants.MaxRetriesがコンパイル時の定数であると仮定してもです。

基本的に、言語仕様には到達可能性のルールがあり、これらのいずれかforが true の場合、ステートメントの終点に到達できます (C# 4 仕様のセクション 8.8.3)。

  • ステートメントには、ステートメントを終了forする到達可能なステートメントが含まれていますbreakfor
  • forステートメントは到達可能であり、for条件が存在し、定数値を持っていませんtrue

ここでは後者の点が該当するため、forステートメントの最後に到達できます。非 void メソッドの最後には到達できないため、エラーが発生します。(C# 仕様のセクション 8.1)

別の方法は、言語をはるかに複雑にすることです。これはコンパイルしなくても大丈夫です。

于 2012-11-08T16:02:17.090 に答える
5

コンパイラは、for ループが常に少なくとも 1 回実行されることを知りません。まったく実行されない有効なオプションと見なされthrowますreturn

プログラムを十分に複雑に分析することで、常にリターンまたはスローを行うことを理論的に証明することは可能ですが、そのような分析は非常に複雑で、パフォーマンスに負荷がかかります。C# コンパイラ チームは、この複雑な分析を追加するために、ミスのリスクを冒したり、コンパイル時間が大幅に増加することを考慮したりしたくありませんでした。彼らは、「到達可能」コードと「到達不能」コードのより単純な定義を使用することを選択しました。これは、実装がより簡単で高速です。

実際の修正に関してthrowは、メソッドの最後に a を追加するだけで、実際にはヒットできないことを示すコメントを入れることができます。

于 2012-11-08T16:01:42.493 に答える
4

C# コンパイラが、この関数が常に返されるかスローされることを認識できないのはなぜですか?

コンパイラは、実行できる分岐を確認するためにプログラムの動作をシミュレートしようとしません。考えられるすべてのブランチを調べるだけです。あなたの場合、何らかの理由remainingTries >= 0で false と評価された場合、ブール値やスローを返さずにメソッドの最後に落ちます。

forループ内のチェックを削除することで修正できます。

public bool DoWorkWithRetry()
{
    for (int remainingTries = Constants.MaxRetries;; remainingTries--)
    {
        // etc...
    }
}

とにかく、そのチェックには有用な目的はありません。

問題を完全に回避するためにメソッドを書き直すこともできます。

for (int remainingTries = Constants.MaxRetries; remainingTries >= 0; remainingTries--)
{
    try
    {
        return DoWork();
    }
    catch (Exception ex)
    {
        // fall through to retry
    }
}

throw new WorkException(
        String.Format("Failed after {0} retries.", Constants.MaxRetries),
        ex);

また、すべての例外をキャッチして飲み込まないでください。知っている特定の例外をキャッチします。考えられるすべての例外をキャッチすることは、悪い考えです。

于 2012-11-08T16:01:53.600 に答える
2

この同等のコードにリファクタリングしても、コンパイラはまだ文句を言いますか?

public bool DoWorkWithRetry()
{
    Exception e;
    for (int remainingTries = Constants.MaxRetries; remainingTries >= 0; remainingTries--)
    {
        try
        {
            return DoWork();
        }
        catch (Exception ex)
        {
            e = ex;
        }
    }
    throw new WorkException(
         String.Format("Failed after {0} retries.", Constants.MaxRetries), e);
}
于 2012-11-08T16:03:58.587 に答える
0

for ループ内にあるものが実行されるかどうかを知るには、賢すぎる必要があります。知るためには、 for ループが実際に実行されていることをテストする必要がありMaxRitries >= 0ます。

于 2012-11-08T16:02:04.903 に答える