一度に複数の例外をキャッチしますか?


2140

単に捕まえるのはお勧めしませんSystem.Exception。代わりに、「既知の」例外のみをキャッチする必要があります。

さて、これは時々不必要な反復的なコードにつながる、例えば:

try
{
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
    WebId = Guid.Empty;
}
catch (OverflowException)
{
    WebId = Guid.Empty;
}

私は疑問に思います:両方の例外をキャッチし、WebId = Guid.Empty呼び出しを1回だけ呼び出す方法はありますか?

与えられた例はだけなので、かなり単純GUIDです。しかし、オブジェクトを複数回変更するコードを想像してください。操作の1つが予期した方法で失敗した場合は、を「リセット」する必要がありobjectます。ただし、予期しない例外が発生した場合でも、さらに高く設定したいと思います。


5
.net 4以降を使用している場合は、aggregateexception msdn.microsoft.com/en-us/library/system.aggregateexception.aspx
Bepenfriends

2
Bepenfriends-ので可能System.GuidがスローされませんAggregateExceptionをあなた(または誰かが)あなたがAggregateExceptionなど。にそれをラップする方法を示して答えを投稿することができれば、それは素晴らしいことだ


11
「単にSystem.Exceptionをキャッチすることはお勧めしません。」-メソッドが32種類の例外をスローできる場合、どうすればよいですか?それらのそれぞれのために別々にキャッチを書くのですか?
giorgim

5
メソッドが32の異なるタイプの例外をスローする場合、そのメソッドは正しく記述されていません。自身の呼び出しが行っている例外をキャッチしていないか、1つのメソッドでFARを過剰に実行しているか、またはそれらの32の過半数またはすべてが理由コードを持つ単一の例外である必要があります。
-Flynn1179

回答:


2100

System.Exceptionタイプをキャッチして切り替える

catch (Exception ex)            
{                
    if (ex is FormatException || ex is OverflowException)
    {
        WebId = Guid.Empty;
        return;
    }

    throw;
}

69
残念ながら、FxCop(つまり-Visual Studio Code Analysis)は、例外をキャッチしたときにそれを好まない。
Andrew Garrison、

15
例外をキャッチしないことに同意しますが、この場合、キャッチはフィルターです。他の例外タイプを処理する上位のレイヤーがある場合があります。catch(Exception x)が含まれていても、これは正しいと言えます。プログラムフローを変更するのではなく、特定の例外を処理するだけで、アプリケーションの残りの部分で他の例外タイプを処理できます。
lkg

28
上記のコードを使用しても、最新バージョンのFxCopは例外をスローしません。
Peter

28
そもそもOPのコードの何が問題だったかわからない。受け入れられた#1の答えは、行数がほぼ2倍になり、読みにくくなります。
ジョアン・ブラガンサ

22
@JoãoBragança:この例のこの回答はより多くの行を使用していますが、たとえばファイルIOを処理している場合、それらの例外をキャッチしていくつかのログメッセージングを実行するだけでよいと想像してみてください。ファイルIOメソッド。次に、より多くの数(約5以上)の異なるタイプの例外を処理する必要があることがよくあります。そのような状況では、このアプローチはいくつかの行を節約できます。
Xilconic 2013年

595

編集:私は、C#6.0以降、例外フィルターは完全に優れた方法であると言っている他の人にも同意します:catch (Exception ex) when (ex is ... || ex is ... )

ただし、1本の長い行のレイアウトはまだ嫌で、個人的には次のようにコードをレイアウトします。理解力が高まると思うので、見た目と同じくらい機能的だと思います。一部は同意しないかもしれません:

catch (Exception ex) when (
    ex is ...
    || ex is ...
    || ex is ...
)

元の:

私はここでのパーティーに少し遅れていることを知っていますが、聖なる煙...

追跡に直行すると、この種の方法は以前の答えを複製しますが、いくつかの例外タイプに対して共通のアクションを実行し、1つのメソッドの範囲内ですべてをきちんと整頓したい場合は、ラムダを使用しないでください。 / closure / inline関数は次のようなことをしますか?つまり、そのクロージャを、あらゆる場所で利用できる個別のメソッドにしたいことに気づく可能性はかなり高いです。しかし、そうすれば、コードの残りの部分を実際に構造的に変更することなく、それを簡単に実行できます。正しい?

private void TestMethod ()
{
    Action<Exception> errorHandler = ( ex ) => {
        // write to a log, whatever...
    };

    try
    {
        // try some stuff
    }
    catch ( FormatException  ex ) { errorHandler ( ex ); }
    catch ( OverflowException ex ) { errorHandler ( ex ); }
    catch ( ArgumentNullException ex ) { errorHandler ( ex ); }
}

私は仕方がないのではないか(警告:少し皮肉/皮肉なのですが)なぜ地球上でこのすべての取り組みに取り組み、基本的に次のものを置き換えるだけなのでしょうか。

try
{
    // try some stuff
}
catch( FormatException ex ){}
catch( OverflowException ex ){}
catch( ArgumentNullException ex ){}

...次のコードのにおいのクレイジーなバリエーションで、つまり、いくつかのキーストロークを節約しているふりをするための例です。

// sorta sucks, let's be honest...
try
{
    // try some stuff
}
catch( Exception ex )
{
    if (ex is FormatException ||
        ex is OverflowException ||
        ex is ArgumentNullException)
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

自動的に読みやすくなるわけではないからです。

確かに、私/* write to a log, whatever... */ return;は最初の例から3つの同一のインスタンスを残しました。

しかし、それは私の主張のようなものです。Y'allは関数/メソッドについて聞いたことがありますか?真剣に。共通のErrorHandler関数を記述し、同様に各catchブロックから呼び出します。

私に尋ねると、2番目の例(ifおよびisキーワードを使用)は、可読性が大幅に低下すると同時に、プロジェクトのメンテナンスフェーズでエラーが発生しやすくなります。

プログラミングに比較的慣れていない可能性がある人にとって、メンテナンスフェーズはプロジェクトの全ライフタイムの98.7%以上を占めることになり、メンテナンスを行う貧しいシュマックはほぼ間違いなくあなた以外の誰かになります。そして、彼らが自分の時間の50%をあなたの名前をののしる仕事に費やす可能性は非常に高いです。

そしてもちろんのFxCopのあなたに、あなたがしなければならないので、鳴き声正確に動作しているプログラムで行うにはジップたあなたのコードに属性を追加し、例99.9%で、それは完全であるという問題を無視するようにFxCopのを伝えることしかありませんフラグ設定で修正します。そして、申し訳ありませんが、私は間違っているかもしれませんが、その「無視」属性が実際にアプリにコンパイルされてしまいませんか?

ifテスト全体を1行にすると、読みやすくなりますか?私はそうは思いません。つまり、別のプログラマーが1行前により多くのコードを配置すると、コードが「より高速に実行される」と強く主張しました。しかし、もちろん彼は荒れ狂う猛烈な勢いでした。インタープリターまたはコンパイラーがその長い行をどのように個別の1行の命令に分解するかを説明しようとすると(まっすぐな面で難しい)、彼が先に進んだ場合の結果と本質的に同じです。コンパイラを巧みにしようとする代わりに、コードを読みやすくしただけで、コンパイラには何の影響もありませんでした。しかし、私は余談です。

今から1か月か2か月後に3つの例外タイプを追加すると、どれほど読みにくくなりますか?(回答:それは取得たくさん読みにくく)。

主要なポイントの1つは、実際に、私たちが毎日見ているテキストのソースコードをフォーマットすることのほとんどのポイントは、コードの実行時に実際に何が起こっているかを他の人間に本当に明確にすることです。なぜなら、コンパイラーはソースコードをまったく異なるものに変換し、コードのフォーマットスタイルを気にしないからです。したがって、オール・オン・ワンの行も完全に最悪です。

ただ言って...

// super sucks...
catch( Exception ex )
{
    if ( ex is FormatException || ex is OverflowException || ex is ArgumentNullException )
    {
        // write to a log, whatever...
        return;
    }
    throw;
}

36
私が最初にこの質問に出くわしたとき、私は受け入れられた答えのすべてに行き当たりました。すべてExceptionのsをキャッチし、タイプをチェックできるだけです。コードを整理したと思ったのですが、何かが原因で質問に戻ってしまい、実際に質問に対する他の回答を読みました。私はしばらくそれを噛んだが、私はあなたに同意しなければならない。それは、より読みやすく、キャッチすべてのものに比べて、あなたのコードを乾燥させる機能を使用して、保守、リスト、ラッピングコード、および投げると比較するタイプをご確認ください。遅れて来て、代わりとなる(IMO)オプションを提供してくれてありがとう。+1。
エラーのある2014年

8
を含めたい場合は、エラー処理関数を使用しても機能しませんthrow;。各catchブロックでそのコード行を繰り返す必要があります(明らかに世界の終わりではありませんが、繰り返す必要のあるコードなので、言及する価値があります)。
kad81 2014年

5
@ kad81、それは本当ですが、ロギングとクリーンアップのコードを1か所で記述し、必要に応じて1か所で変更するという利点が得られます。例外タイプ。そしてthrow();、各catchブロックの1つの追加のステートメントは、IMOのわずかな代金であり、必要に応じて、例外タイプ固有の追加のクリーンアップを実行する立場にあります。
クレイグ、

2
こんにちは@Reitffunk、Func<Exception, MyEnumType>代わりにを使用してくださいAction<Exception>。それだFunc<T, Result>と、Result戻り値の型であること。
Craig

3
ここで完全に同意します。私も最初の答えを読んだので、論理的に思えます。すべての例外ハンドラーの汎用1に移動しました。私の内部の何かが私を内部で吐き気にさせました...それで私はコードを元に戻しました。その後、この美しさに出会いました!これは承認された回答である必要があります
Conor Gallagher

372

他の人が指摘したように、if何が起こっているのかを判断するために、catchブロック内にステートメントを含めることができます。C#6は例外フィルターをサポートしているため、以下が機能します。

try {  }
catch (Exception e) when (MyFilter(e))
{
    
}

MyFilterこの方法は、このようなものになります:

private bool MyFilter(Exception e)
{
  return e is ArgumentNullException || e is FormatException;
}

または、これをすべてインラインで実行することもできます(whenステートメントの右側はブール式でなければなりません)。

try {  }
catch (Exception e) when (e is ArgumentNullException || e is FormatException)
{
    
}

これはifcatchブロック内からステートメントを使用する場合とは異なり、例外フィルター使用してもスタック巻き戻されません

Visual Studio 2015をダウンロードして、これを確認できます。

Visual Studio 2013を引き続き使用する場合は、次のnugetパッケージをインストールできます。

インストールパッケージMicrosoft.Net.Compilers

執筆時点では、これにはC#6のサポートが含まれます。

このパッケージを参照すると、システムにインストールされているバージョンではなく、パッケージに含まれているC#およびVisual Basicコンパイラの特定のバージョンを使用してプロジェクトがビルドされます。


3
6の正式リリースを辛抱強く待っています...これが起こったときに、これがチェキを取得することを期待しています。
RubberDuck、2015

@RubberDuck C#6からのnull伝播演算子が死にそうです。不安定な言語/コンパイラのリスクはそれだけの価値があるとチームの他のメンバーに納得させようとします。大きな影響を伴うマイナーな改善がたくさんあります。答えとしてマークされることについては、重要ではありませんが、これが可能である/可能であると人々が理解している限り、私は幸せです。
Joe

正しい?!近いうちにコードベースをよく見ていきます。=)チェックが重要ではないことはわかっていますが、受け入れられた回答がすぐに古くなるので、OPが戻ってこれをチェックして適切な可視性を与えることを望んでいます。
RubberDuck、2015

それが、私がまだ@Joeを授与していない理由の1つです。これが見えるようにしたい。ただし、わかりやすくするために、インラインフィルターの例を追加することもできます。
RubberDuck

188

残念ながらC#にはありません。これを行うには例外フィルターが必要であり、C#はMSILのその機能を公開していません。VB.NETにはこの機能がありますが、たとえば

Catch ex As Exception When TypeOf ex Is FormatException OrElse TypeOf ex Is OverflowException

あなたができることは、匿名関数を使用してエラー時コードをカプセル化し、それをそれらの特定のcatchブロックで呼び出します:

Action onError = () => WebId = Guid.Empty;
try
{
    // something
}
catch (FormatException)
{
    onError();
}
catch (OverflowException)
{
    onError();
}

26
VB.netがC#に比べていくつかの興味深い利点を持っているという興味深いアイデアと別の例
Michael Stum

47
@MichaelStum その私はほとんどすべてで面白いそれを呼び出すないだろう...構文の種類身震い
MarioDS

17
例外フィルターはc#6で提供されます!roslyn.codeplex.com/discussions/541301の再スロー
Arne Deruwe 14年

@ArneDeruweリンクありがとうございます!私はちょうど再スローにないもう一つの重要な理由を学んだ:throw e;破棄スタックトレースコールスタック、throw;破棄「のみ」のコールスタックは、A(役に立たないクラッシュ・ダンプレンダリング!)非常に良い理由の使用にもないことを避けることができる場合を!
AnorZaken 2014

1
C#6以降では、例外フィルターを使用できます。最終的に。
ダニー

134

完全を期すために、.NET 4.0以降、コードは次のように書き直すことができます

Guid.TryParse(queryString["web"], out WebId);

TryParseは例外をスローすることはなく、形式が正しくない場合はfalseを返し、WebIdをに設定しGuid.Emptyます。


C#7以降、別の行に変数を導入することを回避できます。

Guid.TryParse(queryString["web"], out Guid webId);

返されるタプルを解析するためのメソッドを作成することもできます。これは、バージョン4.6の時点で.NET Frameworkではまだ利用できません。

(bool success, Guid result) TryParseGuid(string input) =>
    (Guid.TryParse(input, out Guid result), result);

そして、次のように使用します。

WebId = TryParseGuid(queryString["web"]).result;
// or
var tuple = TryParseGuid(queryString["web"]);
WebId = tuple.success ? tuple.result : DefaultWebId;

この役に立たない答えに対する次の役に立たない更新は、C#12でアウトパラメーターの分解が実装されたときに行われます。


19
正確に-簡潔であり、例外を処理することによるパフォーマンスのペナルティ、プログラムフローを制御するために意図的に例外を使用することの悪い形、そして変換ロジックを少しずつここにそして少しだけ広げることのソフトフォーカス。
クレイグ

9
私はあなたが何を意味したのか知っていますが、もちろんGuid.TryParse戻りませんGuid.Empty。文字列の形式が正しくない場合、result出力パラメータはに設定されますがGuid.Empty、が返されます false。私はのスタイルで物事を行うコードを見てきましたので、私はそれを言及していGuid.TryParse(s, out guid); if (guid == Guid.Empty) { /* handle invalid s */ }た場合、通常は間違っている、sの文字列表現かもしれないがGuid.Empty

14
質問の趣旨ではないことを除いて、質問に回答しました。より大きな問題は何か他のものです:(
nawfal

6
もちろん、TryParseを使用するための適切なパターンはに似ていますif( Guid.TryParse(s, out guid){ /* success! */ } else { /* handle invalid s */ }。これは、入力値が実際にはGuidの文字列表現である可能性がある壊れた例のような曖昧さを残しません。
クレイグ14

2
この答えは確かにGuid.Parseに関しては正しいかもしれませんが、元の質問のすべてのポイントを逃しています。これはGuid.Parseとは何の関係もありませんでしたが、ExceptionとFormatException / OverflowExceptionなどをキャッチすることに関するものでした。
Conor Gallagher

115

例外フィルターがc#6以降で利用可能になりました。できるよ

try
{
       WebId = new Guid(queryString["web"]);
}
catch (Exception ex) when(ex is FormatException || ex is OverflowException)
{
     WebId = Guid.Empty;
}

C#7.0以降では、これをパターンマッチングと組み合わせることができます

try
{
   await Task.WaitAll(tasks);
}
catch (Exception ex) when( ex is AggregateException ae &&
                           ae.InnerExceptions.Count > tasks.Count/2)
{
   //More than half of the tasks failed maybe..? 
}

この方法は、シンプルで明確なだけでなく、条件が満たされない場合にスタックを巻き戻す必要がないため、優先されます。これにより、再スローと比較してパフォーマンスと診断情報が向上します。
joe

74

アプリケーションをC#6にアップグレードできれば幸運です。新しいC#バージョンには、例外フィルターが実装されています。だからこれを書くことができます:

catch (Exception ex) when (ex is FormatException || ex is OverflowException) {
    WebId = Guid.Empty;
}

このコードは

catch (Exception ex) {                
    if (ex is FormatException || ex is OverflowException) {
        WebId = Guid.Empty;
    }
    throw;
}

しかし、そうではありません。実際、これはC#6の唯一の新機能であり、以前のバージョンではエミュレートできません。まず、再スローは、キャッチをスキップするよりもオーバーヘッドが多いことを意味します。第二に、それは意味的に同等ではありません。この新機能により、コードをデバッグするときにスタックがそのまま保持されます。この機能がなければ、クラッシュダンプはあまり役に立たず、役に立たないことさえあります。

CodePlexでこれに関する議論を参照してください。そして、違いを示す例


4
例外なくスローするとスタックは保持されますが、「throw ex」で上書きされます。
Ivan

32

あなたが使用したくない場合はif内の文をcatch、スコープC# 6.0使用できるException Filters構文すでにプレビューバージョンでCLRでサポートされているだけに存在していたVB.NET/をMSIL

try
{
    WebId = new Guid(queryString["web"]);
}
catch (Exception exception) when (exception is FormatException || ex is OverflowException)
{
    WebId = Guid.Empty;
}

このコードはExceptionInvalidDataExceptionまたはArgumentNullExceptionます。

実際には、基本的にその中に任意の条件を置くことができます when句。

static int a = 8;

...

catch (Exception exception) when (exception is InvalidDataException && a == 8)
{
    Console.WriteLine("Catch");
}

のスコープif内のステートメントとは対照的に、はスローできません。スローする場合、または条件がでない場合、代わりに次の条件が評価されます。catchException FiltersExceptionstruecatch

static int a = 7;

static int b = 0;

...

try
{
    throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
    Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
    Console.WriteLine("General catch");
}

出力:一般的なキャッチ。

複数ある場合true Exception Filter-最初のものが受け入れられます:

static int a = 8;

static int b = 4;

...

try
{
    throw new InvalidDataException();
}
catch (Exception exception) when (exception is InvalidDataException && a / b == 2)
{
    Console.WriteLine("Catch");
}
catch (Exception exception) when (exception is InvalidDataException || exception is ArgumentException)
{
    Console.WriteLine("General catch");
}

出力:キャッチ。

あなたが見ることができるようにし、MSILコードに翻訳されていないif文、これにFilters、とExceptionsでマークされたエリア内からスローすることはできませんFilter 1Filter 2なく、投げるフィルタExceptionの代わりに失敗しますが、また最後の比較値は、前にスタックにプッシュendfilter命令フィルターの成功/失敗を決定します(Catch 1 XOR Catch 2はそれに応じて実行されます):

例外フィルターMSIL

また、具体的GuidにはGuid.TryParseメソッドがあります。


+1は、複数のwhenフィルターを表示し、複数のフィルターを使用したときに何が起こるかを説明します。
steven87vt 2018年

26

C#7を使用すると、switchステートメントの読みやすさを維持しながら、Michael Stumからの回答を改善できます。

catch (Exception ex)
{
    switch (ex)
    {
        case FormatException _:
        case OverflowException _:
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}

そして、C#8をスイッチ式として:

catch (Exception ex)
{
    WebId = ex switch
    {
        _ when ex is FormatException || ex is OverflowException => Guid.Empty,
        _ => throw ex
    };
}

3
これは、2018 IMHOの時点で受け入れられた回答になるはずです。
MemphiZ 2018

6
Mat Jの回答の使用whenは、スイッチよりもはるかにエレガントで適切です。
rgoliveira

@rgoliveira:質問で尋ねられたケースについては、Mat Jの回答がよりエレガントで適切であることに同意します。ただし、例外の種類によって実行するコードが異なる場合や、実際に例外のインスタンスを使用する場合は、読みにくくなります。これらのシナリオはすべて、このswitchステートメントで同等に扱うことができます。
Fabian

1
@Fabian「例外のタイプに応じて実行するコードが異なる場合、または例外のインスタンスを実際に使用する場合」、別のcatchブロックを作成するか、とにかくそれをキャストする必要があります。 。私の経験ではthrow;、あなたのcatchブロック内のaはおそらくコードのにおいです。
rgoliveira 2018年

@rgoliveira:catchブロックでスローを使用しても問題ない場合があります。リンクを参照してください。caseステートメントは実際にはパターンマッチングリンクを使用しているため、破棄演算子リンク(アンダースコア)を変数名で置き換える場合、キャストする必要はありません。誤解しないでください。例外フィルターを使用する方がきれいな方法ですが、複数のcatchブロックを使用すると、中かっこが多数追加されます。
ファビアン

20

受け入れられた回答は受け入れられるようですが、CodeAnalysis / FxCopが一般的な例外タイプをキャッチしているという事実について文句を言うでしょう。

また、「is」演算子はパフォーマンスをわずかに低下させる可能性があります。

CA1800:不必要にキャストしないでください「代わりに「as」演算子の結果をテストすることを検討する」という、そうすると、各例外を個別にキャッチする場合よりも多くのコードが記述されます。

とにかく、ここで私は何をしますか:

bool exThrown = false;

try
{
    // Something
}
catch (FormatException) {
    exThrown = true;
}
catch (OverflowException) {
    exThrown = true;
}

if (exThrown)
{
    // Something else
}

19
ただし、このようにすると、スタックトレースを失うことなく例外を再スローできないことに注意してください。(受け入れ答えを参照マイケルStumさんのコメント)
ルネ・

2
このパターンは、例外を保存することで改善できます(不適切な書式設定を許しなさい-コメントにコードを配置する方法がわかりません):Exception ex = null; {//何かを試すcatch(FormatException e){ex = e; } catch(OverflowException e){ex = e; } if(ex!= null){//他の何かを扱い、exを処理する}
Jesse Weigert 2012年

3
@JesseWeigert:1.バックティックを使用して、テキストの一部に等幅フォントと明るい灰色の背景を与えることができます。2. スタックトレースを含む元の例外を再びスローすることはできません。
オリバー

2
@CleverNeologism isオペレーターの使用がパフォーマンスにわずかな悪影響を与える可能性があることは事実ですが、例外ハンドラーがパフォーマンスの最適化について過度に懸念する場所ではないことも事実です。アプリが例外ハンドラーに非常に多くの時間を費やしているため、パフォーマンスの最適化によってアプリのパフォーマンスに実際の違いが生じる場合は、注意深く検討する必要がある他のコードの問題があります。とは言っても、スタックトレースが失われるため、およびクリーンアップが状況に応じてcatchステートメントから削除されるため、この解決策はまだ好きではありません。
Craig

3
唯一の時間は、isあなたが後で実行する場合は、オペレータが低下性能があるas(それゆえ、彼らが持つルール資格操作を不必要に)。実際にキャストを実行する必要なくキャストをテストしているだけであれば、is演算子はまさに使用したいものです。
saluce 2015年

19

C#6では、例外フィルターを使用することをお勧めします。以下に例を示します。

 try
 {
      throw new OverflowException();
 }
 catch(Exception e ) when ((e is DivideByZeroException) || (e is OverflowException))
 {
       // this will execute iff e is DividedByZeroEx or OverflowEx
       Console.WriteLine("E");
 }

18

これはマットの答えの変形です(これは少しすっきりしていると思います)...次のメソッドを使用します。

public void TryCatch(...)
{
    try
    {
       // something
       return;
    }
    catch (FormatException) {}
    catch (OverflowException) {}

    WebId = Guid.Empty;
}

他の例外はスローされ、コードWebId = Guid.Empty;はヒットしません。他の例外でプログラムをクラッシュさせたくない場合は、他の2つのキャッチの後にこれを追加します。

...
catch (Exception)
{
     // something, if anything
     return; // only need this if you follow the example I gave and put it all in a method
}

-1 WebId = Guid.Emtpy例外がスローされなかった場合に実行されます。
Sepster

4
@sepster「//何か」の後のreturnステートメントがここに含まれていると思います。私はソリューションがあまり好きではありませんが、これは議論の中で建設的な変形です。+1して反対票を取り消す:-)
toong

@Sepster toongは正しい、私はあなたがそこに戻りたい場合はそれを置くと仮定しました...私は私の答えを一般的なものにしようとして、すべての状況に当てはまるようにしました上手。ただし、十分な対策として、return回答にを追加しました。入力いただきありがとうございます。
bsara 2012年

18

ジョセフデイグルの回答は良い解決策ですが、次の構造は少し整然としていてエラーが発生しにくいことがわかりました。

catch(Exception ex)
{   
    if (!(ex is SomeException || ex is OtherException)) throw;

    // Handle exception
}

式を反転することにはいくつかの利点があります。

  • returnステートメントは必要ありません
  • コードはネストされていません
  • ジョセフのソリューションでは式から分離されている「スロー」または「リターン」ステートメントを忘れるリスクはありません。

1行に圧縮することもできます(ただし、それほどきれいではありません)。

catch(Exception ex) { if (!(ex is SomeException || ex is OtherException)) throw;

    // Handle exception
}

編集: C#6.0 の例外フィルタリングにより、構文が少しわかりやすくなり、他に多くの利点がもたらされます現在のソリューションに比べてれます。(最も顕著なのはスタックを無害のままにする)

これは、C#6.0構文を使用して同じ問題がどのように見えるかです。

catch(Exception ex) when (ex is SomeException || ex is OtherException)
{
    // Handle exception
}

2
+1、これが最良の答えです。return条件が反転していることも少し良いですが、主にがないため、上の回答よりも優れています。
DCシャノン2015年

私もそのことを考えていませんでした。いいですね、リストに追加します。
Stefan T

16

@Micheal

コードのわずかに変更されたバージョン:

catch (Exception ex)
{
   Type exType = ex.GetType();
   if (exType == typeof(System.FormatException) || 
       exType == typeof(System.OverflowException)
   {
       WebId = Guid.Empty;
   } else {
      throw;
   }
}

文字列の比較は醜くて遅いです。


21
「is」キーワードを使用しないのはなぜですか?
Chris Pietschmann、

29
@Michael-たとえば、MicrosoftがFormatExceptionから派生したStringTooLongExceptionを導入した場合でも、それはフォーマット例外であり、特定の例外です。それは、「この正確な例外タイプをキャッチする」のセマンティクスが必要か、「文字列の形式が間違っていたことを意味する例外をキャッチする」かによって異なります。
グレッグブナ

6
@Michael - 「キャッチ(FormatException EX)は後者の意味を持っていること。また、ノート、それはFormatException由来何かをキャッチします。
グレッグブナ

14
@アレックス番号。「ex」なしの「throw」は、元のスタックトレースを含む元の例外を持ちます。「ex」を追加すると、スタックトレースがリセットされるため、実際には元の例外とは異なる例外が発生します。他の誰かが私より上手に説明できると思います。:)
サマンサ

13
-1:このコードは非常に壊れやすいです-ライブラリ開発者が交換することが期待できるthrow new FormatException();throw new NewlyDerivedFromFormatException();ライブラリを使用してコードを壊すことなく、それは、誰かが使用する場合を除いて、すべての例外処理の場合のためにも当てはまるだろう==代わりにis(または単にcatch (FormatException))。
Sam Harwell 2013

13

いかがですか

try
{
    WebId = Guid.Empty;
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
}
catch (OverflowException)
{
}

これは、Catch-Codeを完全にTry-Blockに移動できる場合にのみ機能します。しかし、オブジェクトに対して複数の操作を行い、途中で1つは失敗し、オブジェクトを「リセット」するイメージングコード。
Michael Stum

4
その場合、私はリセット関数を追加し、それを複数のキャッチブロックから呼び出します。
モーリス


11

注意と警告:さらに別の種類の機能的なスタイル。

リンクにあるものはあなたの質問に直接答えるものではありませんが、次のように拡張するのは簡単です:

static void Main() 
{ 
    Action body = () => { ...your code... };

    body.Catch<InvalidOperationException>() 
        .Catch<BadCodeException>() 
        .Catch<AnotherException>(ex => { ...handler... })(); 
}

(基本的に、Catchそれ自体を返す別の空のオーバーロードを提供します)

これに対するより大きな問題は、その理由です。コストがここでの利益を上回るとは思わない:)


1
このアプローチの考えられる利点の1つは、例外をキャッチして再スローすることと、キャッチしないこととの間に意味上の違いがあることです。場合によっては、コードは例外捕捉せずに例外対処する必要があります。このようなことはvb.netでは可能ですが、vb.netで記述され、C#から呼び出されるラッパーを使用しない限り、C#では不可能です。
スーパーキャット2013

1
例外をキャッチせずに例外に対処するにはどうすればよいですか?よくわかりません。
nawfal 2013

@nawful ... vbフィルターを使用-関数filt(exは例外として):LogEx(ex):return false ...その後、キャッチ行:filt(ex)をキャッチするex
FastAl

1
@FastAlこれは、例外フィルターがC#6で許可するものではありませんか?
HimBromBeere

@HimBromBeereうん、彼らは直接的な類似物だ
FastAl

9

2015-12-15の更新:C#6 については、https://stackoverflow.com/a/22864936/1718702を参照してください。これはよりクリーンで、言語の標準となっています。

よりエレガントなソリューションを求めている人向け一度キャッチして例外をフィルタリングするためのがために、以下に示す拡張メソッドを使用します。

この拡張機能は、元々他の目的で作成されたライブラリに既にありましたがtype、例外のチェックには完全に機能しました。さらに、imhoは、||ステートメントの束よりもきれいに見えます。また、受け入れられた回答とは異なり、私は明示的な例外処理を好むのでex is ...、派生クラスは親の型に割り当てることができるため、望ましくない動作が発生しました。

使用法

if (ex.GetType().IsAnyOf(
    typeof(FormatException),
    typeof(ArgumentException)))
{
    // Handle
}
else
    throw;

IsAnyOf.cs拡張機能(依存関係の完全なエラー処理の例を参照)

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter matches at least one of the passed in comparisons.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_comparisons">Values to compare against.</param>
        /// <returns>True if a match is found.</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static bool IsAnyOf<T>(this T p_parameter, params T[] p_comparisons)
        {
            // Validate
            p_parameter
                .CannotBeNull("p_parameter");
            p_comparisons
                .CannotBeNullOrEmpty("p_comparisons");

            // Test for any match
            foreach (var item in p_comparisons)
                if (p_parameter.Equals(item))
                    return true;

            // Return no matches found
            return false;
        }
    }
}

完全なエラー処理の例(新しいコンソールアプリにコピーして貼り付けます)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Common.FluentValidation;

namespace IsAnyOfExceptionHandlerSample
{
    class Program
    {
        static void Main(string[] args)
        {
            // High Level Error Handler (Log and Crash App)
            try
            {
                Foo();
            }
            catch (OutOfMemoryException ex)
            {
                Console.WriteLine("FATAL ERROR! System Crashing. " + ex.Message);
                Console.ReadKey();
            }
        }

        static void Foo()
        {
            // Init
            List<Action<string>> TestActions = new List<Action<string>>()
            {
                (key) => { throw new FormatException(); },
                (key) => { throw new ArgumentException(); },
                (key) => { throw new KeyNotFoundException();},
                (key) => { throw new OutOfMemoryException(); },
            };

            // Run
            foreach (var FooAction in TestActions)
            {
                // Mid-Level Error Handler (Appends Data for Log)
                try
                {
                    // Init
                    var SomeKeyPassedToFoo = "FooParam";

                    // Low-Level Handler (Handle/Log and Keep going)
                    try
                    {
                        FooAction(SomeKeyPassedToFoo);
                    }
                    catch (Exception ex)
                    {
                        if (ex.GetType().IsAnyOf(
                            typeof(FormatException),
                            typeof(ArgumentException)))
                        {
                            // Handle
                            Console.WriteLine("ex was {0}", ex.GetType().Name);
                            Console.ReadKey();
                        }
                        else
                        {
                            // Add some Debug info
                            ex.Data.Add("SomeKeyPassedToFoo", SomeKeyPassedToFoo.ToString());
                            throw;
                        }
                    }
                }
                catch (KeyNotFoundException ex)
                {
                    // Handle differently
                    Console.WriteLine(ex.Message);

                    int Count = 0;
                    if (!Validate.IsAnyNull(ex, ex.Data, ex.Data.Keys))
                        foreach (var Key in ex.Data.Keys)
                            Console.WriteLine(
                                "[{0}][\"{1}\" = {2}]",
                                Count, Key, ex.Data[Key]);

                    Console.ReadKey();
                }
            }
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter matches at least one of the passed in comparisons.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_comparisons">Values to compare against.</param>
        /// <returns>True if a match is found.</returns>
        /// <exception cref="ArgumentNullException"></exception>
        public static bool IsAnyOf<T>(this T p_parameter, params T[] p_comparisons)
        {
            // Validate
            p_parameter
                .CannotBeNull("p_parameter");
            p_comparisons
                .CannotBeNullOrEmpty("p_comparisons");

            // Test for any match
            foreach (var item in p_comparisons)
                if (p_parameter.Equals(item))
                    return true;

            // Return no matches found
            return false;
        }

        /// <summary>
        /// Validates if any passed in parameter is equal to null.
        /// </summary>
        /// <param name="p_parameters">Parameters to test for Null.</param>
        /// <returns>True if one or more parameters are null.</returns>
        public static bool IsAnyNull(params object[] p_parameters)
        {
            p_parameters
                .CannotBeNullOrEmpty("p_parameters");

            foreach (var item in p_parameters)
                if (item == null)
                    return true;

            return false;
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter is not null, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentNullException"></exception>
        public static void CannotBeNull(this object p_parameter, string p_name)
        {
            if (p_parameter == null)
                throw
                    new
                        ArgumentNullException(
                        string.Format("Parameter \"{0}\" cannot be null.",
                        p_name), default(Exception));
        }
    }
}

namespace Common.FluentValidation
{
    public static partial class Validate
    {
        /// <summary>
        /// Validates the passed in parameter is not null or an empty collection, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public static void CannotBeNullOrEmpty<T>(this ICollection<T> p_parameter, string p_name)
        {
            if (p_parameter == null)
                throw new ArgumentNullException("Collection cannot be null.\r\nParameter_Name: " + p_name, default(Exception));

            if (p_parameter.Count <= 0)
                throw new ArgumentOutOfRangeException("Collection cannot be empty.\r\nParameter_Name: " + p_name, default(Exception));
        }

        /// <summary>
        /// Validates the passed in parameter is not null or empty, throwing a detailed exception message if the test fails.
        /// </summary>
        /// <param name="p_parameter">Parameter to validate.</param>
        /// <param name="p_name">Name of tested parameter to assist with debugging.</param>
        /// <exception cref="ArgumentException"></exception>
        public static void CannotBeNullOrEmpty(this string p_parameter, string p_name)
        {
            if (string.IsNullOrEmpty(p_parameter))
                throw new ArgumentException("String cannot be null or empty.\r\nParameter_Name: " + p_name, default(Exception));
        }
    }
}

2つのサンプルNUnitユニットテスト

Exceptionタイプのマッチング動作は正確です(つまり、子はその親タイプのいずれにも一致しません)。

using System;
using System.Collections.Generic;
using Common.FluentValidation;
using NUnit.Framework;

namespace UnitTests.Common.Fluent_Validations
{
    [TestFixture]
    public class IsAnyOf_Tests
    {
        [Test, ExpectedException(typeof(ArgumentNullException))]
        public void IsAnyOf_ArgumentNullException_ShouldNotMatch_ArgumentException_Test()
        {
            Action TestMethod = () => { throw new ArgumentNullException(); };

            try
            {
                TestMethod();
            }
            catch (Exception ex)
            {
                if (ex.GetType().IsAnyOf(
                    typeof(ArgumentException), /*Note: ArgumentNullException derrived from ArgumentException*/
                    typeof(FormatException),
                    typeof(KeyNotFoundException)))
                {
                    // Handle expected Exceptions
                    return;
                }

                //else throw original
                throw;
            }
        }

        [Test, ExpectedException(typeof(OutOfMemoryException))]
        public void IsAnyOf_OutOfMemoryException_ShouldMatch_OutOfMemoryException_Test()
        {
            Action TestMethod = () => { throw new OutOfMemoryException(); };

            try
            {
                TestMethod();
            }
            catch (Exception ex)
            {
                if (ex.GetType().IsAnyOf(
                    typeof(OutOfMemoryException),
                    typeof(StackOverflowException)))
                    throw;

                /*else... Handle other exception types, typically by logging to file*/
            }
        }
    }
}

1
言語を強化することは「より優雅」ではありません。多くの場所で、これは実際にメンテナンスの地獄を作成しました。数年後、多くのプログラマーは自分が作成したモンスターを誇りに思っていません。それはあなたが読むのに慣れているものではありません。「ハァッ?」影響、または深刻な「WTF」。時々混乱します。それが行う唯一のことは、後のメンテナンスでそれを処理する必要がある人々のためにコードを把握することをはるかに難しくすることです-たった一人のプログラマーが「賢い」ことを試みたからです。長年にわたって、私はそれらの「賢い」解決策がめったに良い解決策でもないことを学びました。
Kaii 2014年

1
または一言で言えば、言語がネイティブに提供する可能性に固執します。言語のセマンティクスをオーバーライドしようとしないでください。好きではないからです。あなたの同僚(そしておそらく将来の私)は正直に感謝します。
魁夷

また注目すべきは、あなたのソリューションは、C#6の意味を近似when同じように、任意のバージョンのcatch (Exception ex) {if (...) {/*handle*/} throw;}。の真の価値は、例外がキャッチされる前にwhenフィルターが実行されるため、再スローによる費用/スタックの破損を回避できることです。これは、以前はVBおよびMSILからしかアクセスできなかったCLR機能を利用しています。
Marc L.

もっとエレガント?この例は、このような単純な問題には非常に大きく、コードはひどいように見えるので、一見する価値すらありませんでした。このコードを実際のプロジェクトで他人の問題にしないでください。
KthProg 2016

IsAnyOfメソッド全体を簡単に書き直すことができますp_comparisons.Contains(p_parameter)
maksymiuk

7

これらの答えは表面に触れただけだと感じたので、少し深く掘り下げてみました。

だから私たちが本当にしたいのは、コンパイルできないものです、例えば:

// Won't compile... damn
public static void Main()
{
    try
    {
        throw new ArgumentOutOfRangeException();
    }
    catch (ArgumentOutOfRangeException)
    catch (IndexOutOfRangeException) 
    {
        // ... handle
    }

これが必要な理由は、例外ハンドラーがプロセスの後で必要になるものをキャッチしないようにするためです。確かに、例外をキャッチして、何をすべきかを 'if'で確認することができますが、正直に言って、本当にそうしたくないのです。(FxCop、デバッガーの問題、醜さ)

それでは、なぜこのコードはコンパイルされないのでしょうか-そして、どうすればハッキングできるようにハッキングできるのでしょうか。

コードを見ると、本当にやりたいのは通話を転送することです。ただし、MSパーティションIIによると、IL例外ハンドラーブロックはこのように機能しません。これは、「例外」オブジェクトが異なるタイプを持つ可能性があることを意味するため、この場合は理にかなっています。

または、コードでそれを書くために、コンパイラーに次のようなことをするように頼みます(まあ、それは完全に正しくはありませんが、私が推測する最も近いものです):

// Won't compile... damn
try
{
    throw new ArgumentOutOfRangeException();
}
catch (ArgumentOutOfRangeException e) {
    goto theOtherHandler;
}
catch (IndexOutOfRangeException e) {
theOtherHandler:
    Console.WriteLine("Handle!");
}

これがコンパイルされない理由は非常に明白です。「$ exception」オブジェクトにはどのタイプと値がありますか(ここでは変数「e」に格納されています)?コンパイラーがこれを処理する方法は、両方の例外の共通の基本型が「例外」であることに注意することです。変数に両方の例外を含め、それからキャッチされた2つの例外のみを処理します。これをILに実装する方法は、VB.Netで利用可能な「フィルター」としてです。

これをC#で機能させるには、正しい「例外」基本型を持つ一時変数が必要です。コードのフローを制御するために、いくつかのブランチを追加できます。ここに行く:

    Exception ex;
    try
    {
        throw new ArgumentException(); // for demo purposes; won't be caught.
        goto noCatch;
    }
    catch (ArgumentOutOfRangeException e) {
        ex = e;
    }
    catch (IndexOutOfRangeException e) {
        ex = e;
    }

    Console.WriteLine("Handle the exception 'ex' here :-)");
    // throw ex ?

noCatch:
    Console.WriteLine("We're done with the exception handling.");

これの明らかな欠点は、適切に再スローできないことです。正直に言うと、これはかなり醜い解決策です。醜さは、ブランチの削除を実行することで少し修正できます。これにより、ソリューションが少し良くなります。

Exception ex = null;
try
{
    throw new ArgumentException();
}
catch (ArgumentOutOfRangeException e)
{
    ex = e;
}
catch (IndexOutOfRangeException e)
{
    ex = e;
}
if (ex != null)
{
    Console.WriteLine("Handle the exception here :-)");
}

それは「再スロー」だけを残します。これを機能させるには、「catch」ブロック内で処理を実行できる必要があります。これを機能させる唯一の方法は、「Exception」オブジェクトをキャッチすることです。

この時点で、オーバーロード解決を使用してさまざまなタイプの例外を処理する、または例外を処理する別の関数を追加できます。どちらにも欠点があります。まず、ヘルパー関数を使用して行う方法は次のとおりです。

private static bool Handle(Exception e)
{
    Console.WriteLine("Handle the exception here :-)");
    return true; // false will re-throw;
}

public static void Main()
{
    try
    {
        throw new OutOfMemoryException();
    }
    catch (ArgumentException e)
    {
        if (!Handle(e)) { throw; }
    }
    catch (IndexOutOfRangeException e)
    {
        if (!Handle(e)) { throw; }
    }

    Console.WriteLine("We're done with the exception handling.");

他の解決策は、Exceptionオブジェクトをキャッチして、それに応じて処理することです。上記のコンテキストに基づくこれの最も文字通りの翻訳はこれです:

try
{
    throw new ArgumentException();
}
catch (Exception e)
{
    Exception ex = (Exception)(e as ArgumentException) ?? (e as IndexOutOfRangeException);
    if (ex != null)
    {
        Console.WriteLine("Handle the exception here :-)");
        // throw ?
    }
    else 
    {
        throw;
    }
}

結論として:

  • 再スローしたくない場合は、適切な例外をキャッチして一時的に保存することを検討します。
  • ハンドラーが単純で、コードを再利用したい場合、おそらく最良の解決策はヘルパー関数を導入することです。
  • 再スローする場合は、コードを「例外」キャッチハンドラーに配置するしかありません。これにより、FxCopとデバッガーのキャッチされない例外が中断されます。

7

これは、すべてのC#開発者が最終的に直面する古典的な問題です。

あなたの質問を2つの質問に分けましょう。最初、

一度に複数の例外をキャッチできますか?

つまり、いいえ。

次の質問につながります

同じcatch()ブロックで複数の例外タイプをキャッチできない場合に、重複したコードを作成しないようにするにはどうすればよいですか?

フォールバック値を構築するのが安価な特定のサンプルを考えると、次の手順に従います。

  1. WebIdをフォールバック値に初期化します。
  2. 一時変数に新しいGuidを作成します。
  3. WebIdを完全に構築された一時変数に設定します。これをtry {}ブロックの最後のステートメントにします。

したがって、コードは次のようになります。

try
{
    WebId = Guid.Empty;
    Guid newGuid = new Guid(queryString["web"]);
    // More initialization code goes here like 
    // newGuid.x = y;
    WebId = newGuid;
}
catch (FormatException) {}
catch (OverflowException) {}

例外がスローされた場合、WebIdは半分構成された値に設定されることはなく、Guid.Emptyのままです。

フォールバック値の作成にコストがかかり、値のリセットがはるかに安価な場合は、リセットコードを独自の関数に移動します。

try
{
    WebId = new Guid(queryString["web"]);
    // More initialization code goes here.
}
catch (FormatException) {
    Reset(WebId);
}
catch (OverflowException) {
    Reset(WebId);
}

これは素晴らしい「エコロジカルコーディング」です。つまり、コードとデータのフットプリントについて前もって考えており、半分処理された値のリークがないことを確認しています。ジェフリー、ありがとうございます。
Tahir Khalid 2018年

6

では、すべての例外スイッチ内で多くのコードを繰り返していますか?メソッドを抽出するような音は素晴らしいアイデアでしょうね。

したがって、コードは次のようになります。

MyClass instance;
try { instance = ... }
catch(Exception1 e) { Reset(instance); }
catch(Exception2 e) { Reset(instance); }
catch(Exception) { throw; }

void Reset(MyClass instance) { /* reset the state of the instance */ }

なぜ誰もそのコードの重複に気付かなかったのかしら。

C#6からは、すでに他の人が述べたように、さらに例外フィルターがあります。したがって、上記のコードを次のように変更できます。

try { ... }
catch(Exception e) when(e is Exception1 || e is Exception2)
{ 
    Reset(instance); 
}

3
「なぜそのコードの重複に誰も気づかなかったのかしら」-ええと、何?質問の全体のポイントは、コードの重複を排除することです。
Mark Amery 2017年

4

このすでに長いスレッドに私の短い答えを追加したかった。言及されていないものは、catchステートメントの優先順位です。具体的には、キャッチしようとしている各タイプの例外のスコープを認識する必要があります。

たとえば、「catch-all」例外をExceptionとして使用すると、他のすべてのcatchステートメントよりも優先され、明らかにコンパイラエラーが発生しますが、順序を逆にすると、catchステートメントを連鎖させることができます(アンチパターンのビット)キャッチオール例外タイプを下部に配置すると、try..catchブロックで上位に対応しなかった例外がキャプチャされます。

            try
            {
                // do some work here
            }
            catch (WebException ex)
            {
                // catch a web excpetion
            }
            catch (ArgumentException ex)
            {
                // do some stuff
            }
            catch (Exception ex)
            {
                // you should really surface your errors but this is for example only
                throw new Exception("An error occurred: " + ex.Message);
            }

このMSDNドキュメントを確認することを強くお勧めします。

例外階層


4

catch句内にないコードの他の部分と同じように、一般的なコードをメソッドに配置するなど、コードをシンプルに保つようにしてください。

例えば:

try
{
    // ...
}
catch (FormatException)
{
    DoSomething();
}
catch (OverflowException)
{
    DoSomething();
}

// ...

private void DoSomething()
{
    // ...
}

どうやってそれをやるか、シンプルなパターンを見つけることは美しいパターンです


3

私はそれを行う方法を1つ見つけましたが、これはThe Daily WTFの資料のように見えます。

catch (Exception ex)
{
    switch (ex.GetType().Name)
    {
        case "System.FormatException":
        case "System.OverflowException":
            WebId = Guid.Empty;
            break;
        default:
            throw;
    }
}

9
-1投票、+ 5 WTF :-)これは回答としてマークされるべきではありませんでしたが、彼はおかしいです。
アーロン

1
どれだけ簡単にできるかは関係ありません。しかし、彼は怠惰に座らず、それを解決するという彼の見方を思いつきました。本当に感謝しています。
Maxymus 2016年

2
ただし、実際にはこれを行わないでください。C#6で例外フィルターを使用するか、他の回答を使用してください。具体的には、ここに「これは1つの方法ですが、これは悪いことであり、もっと良いことをしたい」と表現しました。
Michael Stum

なぜこれが悪いのですか?例外をswitchステートメントで直接使用できないことに戸惑いました。
MKesper

3
@MKesper私はそれが悪いいくつかの理由を見ます。完全修飾クラス名を文字列リテラルとして書き込む必要があります。これは、コンパイラーがユーザーを救うことができないタイプミスに対して脆弱です。(これは重要です。多くのショップでは、エラーケースは十分にテストされていないため、些細なミスは見逃される可能性が高いためです。)また、指定されたケースの1つのサブクラスである例外と一致しません。また、文字列であるため、VSの「すべての参照を検索」などのツールではケースが見落とされます。特定の例外がキャッチされたすべての場所にクリーンアップ手順を追加する場合に適切です。
Mark Amery 2017年

2

ここで言及する価値があります。複数の組み合わせ(例外エラーと例外。メッセージ)に対応できます。

コンテンツをTextBox、TextBlock、またはCheckBoxとして、データグリッド内のコントロールオブジェクトをキャストしようとすると、ユースケースシナリオに遭遇しました。この場合、返される例外は同じですが、メッセージは異なります。

try
{
 //do something
}
catch (Exception ex) when (ex.Message.Equals("the_error_message1_here"))
{
//do whatever you like
} 
catch (Exception ex) when (ex.Message.Equals("the_error_message2_here"))
{
//do whatever you like
} 

0

私は最短の答えを提案したいと思います(もう1つの機能的なスタイル):

        Catch<FormatException, OverflowException>(() =>
            {
                WebId = new Guid(queryString["web"]);
            },
            exception =>
            {
                WebId = Guid.Empty;
            });

そのためには、System.Actionと同様に、いくつかの「Catch」メソッドオーバーロードを作成する必要があります。

    [DebuggerNonUserCode]
    public static void Catch<TException1, TException2>(Action tryBlock,
        Action<Exception> catchBlock)
    {
        CatchMany(tryBlock, catchBlock, typeof(TException1), typeof(TException2));
    }

    [DebuggerNonUserCode]
    public static void Catch<TException1, TException2, TException3>(Action tryBlock,
        Action<Exception> catchBlock)
    {
        CatchMany(tryBlock, catchBlock, typeof(TException1), typeof(TException2), typeof(TException3));
    }

あとは好きなだけ。しかし、一度それを行う必要があり、すべてのプロジェクトで使用できます(または、nugetパッケージを作成した場合は、それも使用できます)。

そしてCatchManyの実装:

    [DebuggerNonUserCode]
    public static void CatchMany(Action tryBlock, Action<Exception> catchBlock,
        params Type[] exceptionTypes)
    {
        try
        {
            tryBlock();
        }
        catch (Exception exception)
        {
            if (exceptionTypes.Contains(exception.GetType())) catchBlock(exception);
            else throw;
        }
    }

PS私はコードの簡素化のためにnullチェックを入れていません、パラメータ検証を追加することを検討してください。

ps2 catchから値を返したい場合は、同じCatchメソッドを実行する必要がありますが、パラメーターにActionではなく、returnsとFuncを使用します。


-15

tryを呼び出して2回キャッチします。

try
{
    WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
    WebId = Guid.Empty;
}
try
{
    WebId = new Guid(queryString["web"]);
}
catch (OverflowException)
{
    WebId = Guid.Empty;
}

それだけです!


3
ええと。これは質問の目的に反しています。彼は、重複するコードを取り除くためにこの質問をします。この答えは、より重複したコードを追加します。
James Esh

-23

C#6.0では、Exception Filtersは例外処理の改善です

try
{
    DoSomeHttpRequest();
}
catch (System.Web.HttpException e)
{
    switch (e.GetHttpCode())
    {
        case 400:
            WriteLine("Bad Request");
        case 500:
            WriteLine("Internal Server Error");
        default:
            WriteLine("Generic Error");
    }
}

13
この例では、例外フィルターの使用は示していません。
user247702

これは、c#6.0で例外をフィルタリングする標準的な方法です
カシフ

5
例外フィルターとは何かをもう一度見てみましょう。この例では、例外フィルターを使用していません。あなたの1年前に投稿されたこの回答には適切な例があります。
user247702

6
例外フィルタリングの例は次のようになりますcatch (HttpException e) when e.GetHttpCode() == 400 { WriteLine("Bad Request"; }
saluce '11 / 11/13
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.