設計 - 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 FiltersException 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に変換され、 ExceptionsFilter 1Filter 2マークされた領域内からスローすることはできませんが、 Exceptionを投げるフィルタは失敗します。 endfilterコマンドがフィルタの成功/失敗を判断する前にスタックにプッシュされた比較値( Catch 1 XOR Catch 2はそれに従って実行されます)。

また、特にGuidGuid.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#6のサポートが含まれます。

このパッケージを参照すると、システムにインストールされているバージョンとは異なり、パッケージに含まれている特定のバージョンの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");
}






exception-handling