6

まず第一に、標準的な答えは、フロー制御に例外を使用することは決してないということです。私はこれに完全に同意しますが、私が時々行ったことについて長い間考えてきました。これを次の擬似コードで説明します。

try
    string keyboardInput = read()
    int number = int.parse(keyboardInput)
    //the conversion succeeds
    if(number >= 1000) 
        //That's not what I asked for. The message to display to the user
        //is already in the catch-block below.
        throw new NumberFormatException() //well, there IS something wrong with the number...
 catch(NumberFormatException ex)  //the user entered text
    print("Please enter a valid number below 1000.")

まず、この例を非常に抽象的な方法で取り上げます。これは、必ずしも発生する必要はありません。状況は単に次のとおりです。

ユーザー入力は制限する必要があり、言語が定義するスローされた例外またはチェックのいずれかによって、2 つの方法で問題が発生する可能性があります。どちらのエラーも同じ方法で報告されます。これは、エラーの原因の技術的な違いを知る必要がないためです。

私はそれを解決するいくつかの方法を考えました。まず、カスタムメイドの例外をスローする方がよいでしょう。次に直面する問題は、ローカルでキャッチした場合、他の例外をどうするかということです。この場合、カスタム例外は 2 番目の catch-block の原因となり、メッセージも同様にコピーされます。私の解決策:

//number is wrong
throw new MyException()
catch(NumberFormatException ex) 
    throw new MyException()
catch(MyException ex) {
    print("Please enter...")

例外の名前の意味はすべてここにあります。このカスタム例外の適用は広く受け入れられていますが、基本的には最初の方法と何も変わっていません。つまり、標準ライブラリの例外ではなくカスタム例外をスローすることによって、強制的に catch ブロックに入る必要がありました。

呼び出し元のメソッドに例外をスローするのと同じ方法 (つまり、カスタム例外の catch ブロックを持たない) の方が理にかなっているようです。私の方法は、技術的には2 つの方法で失敗する可能性がありますが、本質的には 1 つの方法である、間違ったユーザー入力です。したがって、 a を記述しUserInputExceptionて、メソッドに this をスローさせます。新しい問題: これがアプリケーションのメインメソッドである場合はどうなるでしょうか?

私は現在、この種の動作を実装する特定のアプリケーションに苦労していません。私の質問は純粋に理論的であり、言語に固有のものではありません。

これにアプローチする最良の方法は何ですか?

4

6 に答える 6

5

最初の例外は低レベルであると見なし、呼び出しの時点で(この場合は変換によって)処理します。これにより、処理する例外の種類が少なくなるため、後で保守およびリファクタリングが容易になるコードが得られることがわかりました。

try
  string keyboardInput = read()
  try
    int number = int.parse(keyboardInput)
  catch(NumberFormatException ex)
    throw MyException("Input value was not a number")

  //the conversion succeeds
  if(number >= 1000) 
    throw MyException("Input value was out of range")

catch(MyException ex)  //the user entered text
  print( ex.ToString() )
  print("Please enter a valid number below 1000.")
于 2012-05-15T02:37:29.907 に答える
1

私の見方はこれです:

例外をスローしないintを解析する他の方法がないと仮定すると、現在のコードは正しく、エレガントです。

唯一の問題は、コードがある種のループにある場合です。その場合、不要な例外をスローしてキャッチするオーバーヘッドについて心配する可能性があります。その場合、必要な場合にのみ例外を処理することを優先して、コードの美しさの一部を妥協する必要があります。

error=false;

try {
    string keyboardInput = read();
    int number = int.parse(keyboardInput);
    //the conversion succeeds
    if(number >= 1000) {
        //That's not what I asked for. The message to display to the user
        //is already in the catch-block below.
        error=true;
} catch(NumberFormatException ex) { //the user entered text
    error=true;
}

if (error)
    print("Please enter a valid number below 1000.");

また、2つのエラーを1つに集約しようとしている理由についても考えることができます。代わりに、ユーザーがどのようなエラーを実行したかをユーザーに通知できます。これは、場合によってはさらに役立つ可能性があります。

try {
    string keyboardInput = read();
    int number = int.parse(keyboardInput);
    //the conversion succeeds
    if(number >= 1000) {
        //That's not what I asked for. The message to display to the user
        //is already in the catch-block below.
        print("Please enter a number below 1000.");

} catch(NumberFormatException ex) { //the user entered text
    print("Please enter a valid number.");
}
于 2012-05-09T10:00:05.557 に答える
1

この答えはここのどこにも見当たらないので、別の観点から投稿します。

ご存知のように、ルールを十分に理解していれば実際にルールを破ることができるため、状況に最適なソリューションであることがわかっている場合は、フロー制御に例外をスローすることを使用できます私が見たところ、それは通常、いくつかのダムフレームワークで発生します...

とはいえ、Java 7 (強力なコンストラクトをもたらした) より前は、これがコードの繰り返しを避けるための私のアプローチでした。multicatch

try {
    someOffendingMethod();
} catch (Exception e) {
    if (e instanceof NumberFormatException || e instanceof MyException) {
        System.out.println("Please enter a valid number.");
    }
}

これは C# でも有効な手法です。

于 2012-05-21T19:22:48.657 に答える
1

コードの重複を最小限に抑えることを念頭に置いて、基本的にいくつかの方法があると思います。

  1. ブール変数を使用して例外を保存します。実行している特定のタスクの一般的なロジックのどこかにエラーがあった場合、エラーの最初の兆候で終了し、別のエラー処理ブランチでそれを処理します。

    利点: エラーを処理する場所が 1 つだけです。任意のカスタム例外/エラー条件を使用できます。

    短所: 達成しようとしているもののロジックを発見するのが難しい場合があります。

  2. エラーについてユーザーに通知するために使用できる一般的な関数を作成します (ユーザーに表示するメッセージなど、一般的なエラーを説明するすべての情報を事前に計算/保存する)。起こります。

    利点: コードの読者にとって、意図のロジックがより明確になる可能性があります。好きなカスタム例外/エラー条件を使用できます。

    短所: エラーは別の場所で処理する必要があります (ただし、事前に計算/保存された値では、コピーと貼り付けはあまりありませんが、ユーザー部分への通知は複雑です)。

  3. 意図が明確であれば、try ブロック内から明示的に例外をスローすることは悪い考えではないと思います。システム提供の例外のいずれかをスローしたくない場合は、それらのいずれかから派生した独自の例外をいつでも作成できるため、最小限の数 (できれば 1 つ) の catch ブロックのみが必要です。

    利点: エラー状態を処理する場所は 1 か所だけです。つまり、try ブロックでスローされる例外のタイプが本質的に 1 つだけの場合です。

    欠点: 複数のタイプの例外がスローされる場合、ネストされた try-catch ブロック (例外を最も外側のブロックに伝播するため) または非常に一般的な (例: Exception) catch ブロックが必要で、エラー報告の重複を避ける必要があります。

于 2012-05-15T02:07:49.533 に答える
1

この特定の例では、例外は必要ありません。

int number;
if (int.TryParse(keyboardInput, out number) && number < 1000) // success
else // error

ただし、説明する状況はビジネスソフトウェアでは一般的であり、例外をスローして均一なハンドラーに到達することは非常に一般的です。

そのようなパターンの 1 つは、XML 検証とそれに続く XSLT です。一部のシステムでは、無効な XML は検証例外をキャッチすることによって処理されます。これらのシステムでは、XSLT で既存の例外処理を再利用するのはごく自然なことです (特定の検証言語では検出できないいくつかのクラスのデータ エラーを自然に検出できます)。

<xsl:if test="@required = 'yes' and @prohibited = 'yes'>
    <xsl:message terminate='yes'>Error message</xsl:message>
</xsl:if>

そのような状況が非常にまれである場合 (初期の統合テスト中にのみ発生し、他のモジュールの欠陥が修正されると消えると予想される)、フロー制御に例外を使用しないことに関する一般的な懸念のほとんどが実際には当てはまらないことを確認することが重要です。

于 2012-05-09T09:58:58.263 に答える