none
try catch 使用頻度について(使い方) RRS feed

  • 質問

  • 例外処理 try catch の使い方について質問があります。

    try-catchは、どうしようもない場合にのみ使用する物であり「通常は例外が発生しないように作るべきだ」と先輩にお聞きしました。
    ですが、どうしても納得できず質問をしてみたのですが、あまり納得のできる回答を頂けませんでした

    そこで、このコミュニティにいる方々にもお聞きしたいと思い投稿しています。

    try-catchは、頻繁に使用してはダメだというルールなどあるのでしょうか?
    WEBではそういった記載は見受けられませんでした。

    例えば、File.copyなど戻り値の定義はありませんが、例外処理は存在します。
    https://docs.microsoft.com/ja-jp/dotnet/api/system.io.file.copy?view=net-5.0

    そういった場合、File.copyを実施する前にファイルが存在するか?パスが存在するか?など、すべての例外をチェックしたうえでFile.copyを実施するべきなのでしょうか?

    以下のコードで完結にまとめてしまえばいいと思ってしまいます。
    もちろんエラー内容(例外処理)の詳細を知らなくてもいい場合です。

    try
    {
        File.copy( strPathA, strPathB );
    }
    catch
    {
        // エラーメッセージなど.
    }
    


    2021年5月19日 10:54

回答

  • .NET アプリの例外処置について Microsoft Bolg に書いてあったことを要約して紹介しておきます(Bolg は今はリンク切れです)。

    (1) 予測可能で正しい業務フローに戻すことができる「業務エラー」(例:ユーザーの入力間違い)と、予測できないもしくは予測はできても何の対応もできない「例外」(例:DB サーバーダウン)を区別して対処。

    (2) 「例外」はランタイムに拾わせてアプリケーションを停止させる。無かったことにして、ユーザが作業を続けられるようにすると、強制的に停止させるより好ましからざる状況に陥るかも(ユーザーが大事なデータを壊したりとか)。

    (3) よほどのことがない限り try-catch は書かない。

    (4) キャッチせざるを得ない場合でも Execption はキャッチしない。キャッチせざるを得ないとしても範囲を絞る。例えば DB 関係の例外が予測されるなら SqlException に限定して catch し、Number プロパティなどでエラーの内容を調べて対処するとか。

    (5) 間違って補足してしまった例外は throw する。(注:catch ブロックでキャッチした例外を throw するとスタックトレースが途切れるので単に throw と書く)

    (6) ユーザーへの通知が必要なら、集約的例外処置を利用する。

    それから、.NET 4 からは破損状態例外は catch できなくなっているそうですが、「それでも Catch (Exception e) を使用するのはよくない」ということについては以下の記事を見てください。

    破損状態例外を処理する
    https://docs.microsoft.com/ja-jp/archive/msdn-magazine/2009/february/clr-inside-out-handling-corrupted-state-exceptions

    自分が持っている Microsoft の本に、上に書いた原則に則ったサンプルが載っていたので紹介しておきます。

    データベースに INSERT する際に PK 制約違反で発生する例外のみ catch して「業務エラー」(2 重登録)としてユーザーに再入力を促し、その他は再 throw してランタイムに拾わせてアプリケーションを停止させるというものです。

    public bool InsertAuthors()
    {
        var connection = new SqlConnection("接続文字列");
        var command = new SqlCommand("INSERT INTO authors VALUES ('172-32-1176', 'White', ...)", connection);
        try
        {
            connection.Open();
            try
            {
                command.ExecuteNonQuery();
            }
            catch(SqlException sqle)
            {
                if (sqle.Number == 2627)
                {
                    return false;
                }
                else
                {
                    throw;
                }
            }
        }
        finally
        {
            connection.Close();
        }
    
        return true;
    }

    以下の記事も参考になると思います。

    例外の推奨事項
    https://docs.microsoft.com/ja-jp/dotnet/standard/exceptions/best-practices-for-exceptions

    2021年5月19日 13:24

すべての返信

  • .NET アプリの例外処置について Microsoft Bolg に書いてあったことを要約して紹介しておきます(Bolg は今はリンク切れです)。

    (1) 予測可能で正しい業務フローに戻すことができる「業務エラー」(例:ユーザーの入力間違い)と、予測できないもしくは予測はできても何の対応もできない「例外」(例:DB サーバーダウン)を区別して対処。

    (2) 「例外」はランタイムに拾わせてアプリケーションを停止させる。無かったことにして、ユーザが作業を続けられるようにすると、強制的に停止させるより好ましからざる状況に陥るかも(ユーザーが大事なデータを壊したりとか)。

    (3) よほどのことがない限り try-catch は書かない。

    (4) キャッチせざるを得ない場合でも Execption はキャッチしない。キャッチせざるを得ないとしても範囲を絞る。例えば DB 関係の例外が予測されるなら SqlException に限定して catch し、Number プロパティなどでエラーの内容を調べて対処するとか。

    (5) 間違って補足してしまった例外は throw する。(注:catch ブロックでキャッチした例外を throw するとスタックトレースが途切れるので単に throw と書く)

    (6) ユーザーへの通知が必要なら、集約的例外処置を利用する。

    それから、.NET 4 からは破損状態例外は catch できなくなっているそうですが、「それでも Catch (Exception e) を使用するのはよくない」ということについては以下の記事を見てください。

    破損状態例外を処理する
    https://docs.microsoft.com/ja-jp/archive/msdn-magazine/2009/february/clr-inside-out-handling-corrupted-state-exceptions

    自分が持っている Microsoft の本に、上に書いた原則に則ったサンプルが載っていたので紹介しておきます。

    データベースに INSERT する際に PK 制約違反で発生する例外のみ catch して「業務エラー」(2 重登録)としてユーザーに再入力を促し、その他は再 throw してランタイムに拾わせてアプリケーションを停止させるというものです。

    public bool InsertAuthors()
    {
        var connection = new SqlConnection("接続文字列");
        var command = new SqlCommand("INSERT INTO authors VALUES ('172-32-1176', 'White', ...)", connection);
        try
        {
            connection.Open();
            try
            {
                command.ExecuteNonQuery();
            }
            catch(SqlException sqle)
            {
                if (sqle.Number == 2627)
                {
                    return false;
                }
                else
                {
                    throw;
                }
            }
        }
        finally
        {
            connection.Close();
        }
    
        return true;
    }

    以下の記事も参考になると思います。

    例外の推奨事項
    https://docs.microsoft.com/ja-jp/dotnet/standard/exceptions/best-practices-for-exceptions

    2021年5月19日 13:24
  • (Bolg → Blog)

    .NET アプリの例外処置について Microsoft Bolg に書いてあったことを要約して紹介しておきます(Bolg は今はリンク切れです)。

    blogs.msdn.microsoft.com で公開されていた nakama さんの「とあるコンサルタントのつぶやき」のアーカイブ記事を拾い集めてみました。

    同ブログの移転先は nakama.azurewebsites.net で、過去記事の一部は docs.microsoft.com にありますが、欠損しているものは Internet Archive に遺っていたものを拾いました。

    が付いたアーカイブ記事は、ページのロード中に Esc キー等で読み込みを中断させないと、SkyDrive(現 OneDrive) 上に置かれた zip に遷移してしまいます。ご注意ください。

    2021年5月20日 2:33
  • まず、

    ・ エラー等の分岐はユーザーのために実装されるべき

    だと考えますので、全て例外で良いというのは実装側の便宜を優先しすぎかもしれません。

    また、ちょっと違う側面の話なのですが、

    ・ 最初から例外をエラー分岐の代わりに使っているとバグがみすごされやすい

    という経験をしました。テストの自動化等で改善はできるのでしょうが、
    個人的には開発中は落ちるか止まるかしてくれた方が心が平和ですね。
    2021年5月20日 3:07
  • 学習者の立場で意見を述べさせていただきますが、
    try-catchは極力書かないようにしています。

    ただ、どうしても使わなければならない処理というものに一度遭遇しました。
    ファイルの読み込みで、最終行かどうかを判断するのに例外発生を使うしか方法がありませんでした。

    これ以外は、例外が発生する要因を細かく分析すればtry-catchをあてにしなくてもプログラムが書けるようになります。
    2021年5月20日 3:16
  • Hoshina です
    こんにちは

    try-catch の使い方に関しては、SurferOnWww さんの投稿が良くまとまっていると思います。
    丁寧な投稿内容で、うれしいですね。

    それとは、別に使用する頻度に関して。
    私が最近書いているプログラムで、try-catch (ないし、それに該当する処理)を使用しないプログラムはほとんどないように思います。
    そのため、その使い方が適切かどうかの議論は別として、
    try-catch を使用することに、こだわりを感じていません


    それでは

    2021年5月21日 5:01
  • LvZ_012017さん、こんにちは。フォーラムオペレーターのKumoです。 
    MSDNフォーラムにご投稿くださいましてありがとうございます。

    ご質問いただいた件ですが、その後いかがでしょうか。
    皆様から寄せられた投稿はお役に立ちましたか。

    参考になった投稿には [回答としてマーク] をお願い致します。

    設定いただくことで、
    他のユーザーもお役に立つ回答を見つけやすくなります。

    お手数ですが、ご協力の程どうかよろしくお願いいたします。

    MSDN/ TechNet Community Support Kumo ~参考になった投稿には「回答としてマーク」をご設定ください。なかった場合は「回答としてマークされていない」も設定できます。同じ問題で後から参照した方が、情報を見つけやすくなりますので、 ご協力くださいますようお願いいたします。また、MSDNサポートに賛辞や苦情がある場合は、MSDNFSF@microsoft.comまでお気軽にお問い合わせください。~

    2021年5月24日 6:22
    モデレータ
  • 返答が遅くなり申し訳ありません。

    詳細な説明有難うございます。

    大変ためになる内容でした。

    こちらをマークさせて頂ければと思います。

    2021年6月25日 1:58