設計 - try catch 複数 c#
一度に複数の例外をキャッチしますか? (18)
単に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
を「リセット」したいと考えていobject
。 しかし、予期しない例外がある場合、私はまだそれをより高く投げたい。
警告と警告: さらに別の種類、機能的なスタイル。
リンクにあるものはあなたの質問に直接答えませんが、それを次のように拡張するのは簡単です:
static void Main()
{
Action body = () => { ...your code... };
body.Catch<InvalidOperationException>()
.Catch<BadCodeException>()
.Catch<AnotherException>(ex => { ...handler... })();
}
(基本的に、それ自身を返す別の空のCatch
オーバーロードを提供する)
これに対する大きな疑問がなぜあるのか 。 私はコストがここの利益を上回るとは思わない:)
@Micheal
コードを少し修正したもの:
catch (Exception ex)
{
Type exType = ex.GetType();
if (exType == typeof(System.FormatException) ||
exType == typeof(System.OverflowException)
{
WebId = Guid.Empty;
} else {
throw;
}
}
文字列の比較は醜く遅いです。
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");
}
C#7 では 、switchステートメントの可読性を維持しながら、Michael Stumの答えを改善することができます。
catch (Exception ex)
{
switch (ex)
{
case FormatException _:
case OverflowException _:
WebId = Guid.Empty;
break;
default:
throw;
}
}
System.Exception
をキャッチし、型をオンにする
catch (Exception ex)
{
if (ex is FormatException || ex is OverflowException)
{
WebId = Guid.Empty;
return;
}
throw;
}
catch
スコープ内でif
ステートメントを使用しない場合は、 C# 6.0
では、プレビューバージョンで既にCLRでサポートされていたが、 VB.NET
/ MSIL
のみに存在するException Filters
構文を使用できます 。
try
{
WebId = new Guid(queryString["web"]);
}
catch (Exception exception) when (exception is FormatException || ex is OverflowException)
{
WebId = Guid.Empty;
}
このコードは、 InvalidDataException
またはArgumentNullException
の場合にのみException
をキャッチしException
。
実際には、基本的にそのwhen
節の中に任意の条件を入れることができます:
static int a = 8;
...
catch (Exception exception) when (exception is InvalidDataException && a == 8)
{
Console.WriteLine("Catch");
}
catch
のスコープ内のif
文とは対照的に、 Exception Filters
はException Filters
スローすることはできません。 Exceptions
が発生した場合、または条件がtrue
でないtrue
は、次のcatch
条件が代わりに評価されます。
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
が2つ以上ある場合、最初の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 1
とFilter 2
マークされた領域内からスローすることはできませんが、 Exception
を投げるフィルタは失敗します。 endfilter
コマンドがフィルタの成功/失敗を判断する前にスタックにプッシュされた比較値( Catch 1
XOR Catch 2
はそれに従って実行されます)。
また、特にGuid
はGuid.TryParse
メソッドを持っています。
C#6.0では、例外フィルタは例外処理の改善
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");
}
}
catch節の中にないコードの他の部分と同じように、コードを単純なものに保つようにしてください。
例えば:
try
{
// ...
}
catch (FormatException)
{
DoSomething();
}
catch (OverflowException)
{
DoSomething();
}
// ...
private void DoSomething()
{
// ...
}
ちょうど私がそれをする方法は、シンプルで美しいパターンを見つけることを試みる
いかがですか
try
{
WebId = Guid.Empty;
WebId = new Guid(queryString["web"]);
}
catch (FormatException)
{
}
catch (OverflowException)
{
}
これはマットの答えの変形です(私はこれがちょっときれいだと感じています)...方法を使用してください:
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
}
アプリケーションを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の唯一の新機能であり、以前のバージョンではエミュレートできませんでした。 第1に、再スローとは、キャッチをスキップするよりオーバーヘッドを意味します。 第二に、それは意味的に同等ではない。 新しい機能は、コードをデバッグするときにスタックをそのまま保持します。 この機能がなければ、クラッシュダンプはそれほど役に立ちません。
roslyn.codeplex.com/discussions/541301参照してください。 そしてその違いを示す例 。
他の人が指摘しているように、何が起きているのかを判断するためにcatchブロックの中にif
文を置くことができます。 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)
{
…
}
これは、 catch
ブロック内のif
文を使用するif
と異なり、例外フィルタを使用するとスタックを巻き戻しません 。
Visual Studio 2015をダウンロードしてこれを確認することができます。
Visual Studio 2013を引き続き使用する場合は、次のナゲットパッケージをインストールできます。
インストールパッケージMicrosoft.Net.Compilers
このパッケージを参照すると、システムにインストールされているバージョンとは異なり、パッケージに含まれている特定のバージョンのC#およびVisual Basicコンパイラを使用してプロジェクトがビルドされます。
完全性のために、 .NET 4.0以降ではコードを次のように書き換えることができます 。
Guid.TryParse(queryString["web"], out WebId);
TryParseは例外をスローせず、フォーマットが間違っている場合はfalseを返し、WebIdをGuid.Empty
に設定しGuid.Empty
。
C#7以降、別の行に変数を導入することを避けることができます:
Guid.TryParse(queryString["web"], out Guid webId);
戻り値のタプルを解析するメソッドを作成することもできます。これは、.NET Frameworkではまだ使用できません。バージョン4.6以降:
(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;
次に、この無駄な答えに対する無駄な更新は、out-parametersの分解をC#12で実装したときに起こります。
残念ながら、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();
}
この長いスレッドに私の短い答えを加えたいと思っていました。言及されていないものは、catchステートメントの優先順位です。より具体的には、catchしようとしている各タイプの例外の範囲を認識する必要があります。
たとえば、あなたのように「キャッチオール」の例外を使用する場合、例外は、他のすべてのcatchステートメントをpreceedし、注文することができますチェーンアップあなたのcatchステートメントを(私が思うアンチパターンのビットを反転させる場合には、明らかしかし、コンパイルエラーを取得します)あなたは一番下にcatch-all Exception型を置くことができ、これはあなたの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ドキュメントを検討することをお勧めします:
だから、あなたはすべての例外スイッチ内でたくさんのコードを繰り返していますか?方法を抽出するような音は神のアイデアだろうか?
だからあなたのコードはこれになります:
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からは、すでに他の人に言及されているexception-filtersがあります。上記のコードをこれに変更することができます:
try { ... }
catch(Exception e) when(e is Exception1 || e is Exception2)
{
Reset(instance);
}
私はそれを行う一つの方法を見つけたことに注意してください、しかしこれはDaily The WTFのための材料のように見えます:
catch (Exception ex)
{
switch (ex.GetType().Name)
{
case "System.FormatException":
case "System.OverflowException":
WebId = Guid.Empty;
break;
default:
throw;
}
}
catch (Exception ex)
{
if (!(
ex is FormatException ||
ex is OverflowException))
{
throw;
}
Console.WriteLine("Hello");
}