C#で一般的な例外のすべてのバリアントをキャッチする方法


22

一般的な例外クラスのすべてのバリアントをキャッチしたいと思います。複数のcatchブロックなしでそれを行う方法があるかどうか疑問に思っていました。たとえば、例外クラスがあるとします。

public class MyException<T> : Exception
{
    public string MyProperty { get; }

    public MyException(T prop) : base(prop.ToString())
    {
        MyProperty = prop?.ToString();
    }
}

および2つの派生クラス:

public class MyDerivedStringException : MyException<string>
{
    public MyDerivedStringException(string prop) : base(prop)
    {

    }
}

public class MyDerivedIntException : MyException<int>
{
    public MyDerivedIntException(int prop) : base(prop)
    {

    }
}

両方MyDerivedStringExceptionMyDerivedIntException1つのキャッチブロックでキャッチする方法はありますか。

私はこれを試しました:

try
{
   ...
}

catch(Exception e) when (e is MyDerivedStringException || e is MyDerivedIntException)
{
}

しかし、それはあまりきれいではなく、私はにアクセスできませんMyProperty

私は問題の一般的な解決策に興味がありますが、私の場合、一般的な例外はサードパーティのライブラリで定義されています。

回答:


15

MyException<T>インターフェースを実装させ、インターフェースのタイプによって例外をチェックします。

インターフェース:

public interface IMyException
{
    string MyProperty { get; }
}

インターフェイスを実装するジェネリッククラス:

public class MyException<T> : Exception, IMyException
{
    public string MyProperty { get; }

    public MyException(T prop)
    {
        MyProperty = prop?.ToString();
    }
}

派生クラス:

public class MyDerivedStringException : MyException<string>
{
    public MyDerivedStringException(string prop) : base(prop)
    {

    }
}

public class MyDerivedIntException : MyException<int>
{
    public MyDerivedIntException(int prop) : base(prop)
    {

    }
}

使用法:

try
{
    // ...
}
catch (Exception e) when (e is IMyException)
{
    // ...
}

同じことは、その基本クラスから継承しExceptionMyException<T>その基本クラスから派生させる基本クラスを作成することでも実行できます。


3
ここでインターフェイスの価値があるかどうかはわかりませんが、との間の非ジェネリック基本クラスだけで問題ExceptionありMyException<T>ません。
Jon Skeet、

また、OPはまだジェネリックプロパティにアクセスできず(リフレクションを使用しない)、これが本当の問題だと思います。
DavidG

10

Typeジェネリックから派生した場合のテストは次のとおりです。

Type = typeof(something);
t.GetGenericTypeDefinition()==typeof(MyException<>);

しかし、これは唯一のような派生型自身のために真ですMyException<int>MyException<string>

あなたがMyDerivedStringExceptionテストしなければならなかったようなさらなる派生物がある場合:

ex.GetType.BaseType.GetGenericTypeDefinition()==typeof(MyException<>);

したがって、これは既存のジェネリックすべてで機能しますが、このテストの継承レベルを知るか、すべての基本型をループする必要があります。

だからあなたはこれを行うことができます:

catch(Exception ex) when (ex.GetType.BaseType.GetGenericTypeDefinition()==typeof(MyException<>))

3

この実装は、T:ValueがValuetypeの場合にボックス化およびボックス化解除しますが、使用率に関しては、ボックス化/ボックス化解除を試行する回数によってパフォーマンスへの影響を制御できます。

public class MyException<T> : MyException
{
    public T Prop => (T)base.Prop;

    public MyException(T prop) : base(prop)
    {

    }
}

public class MyException : Exception
{
    protected object Prop { get; }

    public MyException(object prop)
    {
         Prop = prop;
    }
}

論理

try {
     ...
} catch(MyException e) {
    ...
}

これは良い答えであり、私は賛成しました-残念ながら、一般的な例外はサードパーティのライブラリにあるため編集できませんので、私の特定の問題は解決しません。
SBFrancies

2

残念ながら、あなたは

catch(MyException ex) それは型を期待するので、クラス。

あなたの最善の策は、基本クラスまたはインターフェイスを作成し、それをキャッチして、そこから型を取得することです。

上の答えはすでにありますので、ここで型を取得し、これを行う方法については。


1

この実装は、T / Cのコンテキストから離れた例外タイプの処理デリゲートを抽象化します...これは少し冒険的すぎるかもしれませんが、再利用の範囲とコンテキストに応じて異なるハンドラーを注入できるというのが素晴らしい利用。

public interface IExceptionHandler
{
    object Handle(Exception ex);

    void RegisterExceptionTypeHandler<T>(Func<T,object> handlerDelegate) where T : Exception;
}

登録ロジック

handler.RegisterExceptionTypeHandler<MyException<int>>(ex => ...);
handler.RegisterExceptionTypeHandler<MyException<string>>(ex => ...);

try {
    ...
}catch(Exception ex)
{
    ...any validation
    return exceptionHandler.Handle(ex)
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.