例外に関する追加情報を提供するにはどうすればよいですか?


20

例外に関する追加情報を提供する必要があるたびに、実際にこれを行う正しい方法はどれかと思います。


この質問のために、例を作成しました。Abbreviationプロパティを更新するクラスがあると仮定しましょう。SOLIDの観点からは完全ではないかもしれませんが、DIを介してワーカーメソッドをいくつかのサービスで渡しても同じ状況が発生します-コンテキストが存在しないという例外が発生します。例に戻る...

class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Abbreviation { get; set; }
}

次に、クラスのインスタンスと、worker-methodが呼び出されるループがあります。それは投げることができStringTooShortExceptionます。

var persons =
{
    new Person { Id = 1, Name = "Fo" },
    new Person { Id = 2, Name = "Barbaz" },
}

public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
    foreach (var person in persons)
    {
        try
        {
            person.Abbreviation = GenerateAbbreviation(person.Name);
        }
        catch(Exception ex)
        {
            // ?
        }
    }
    // throw AggregateException...
}

public IEnumerable<string> GenerateAbbreviation(string value)
{
    if (value.Length < 5)
    {
        throw new StringTooShortException(value);
    }

    // generate abbreviation
}

問題は:PersonまたはId(またはその他)を追加する方法ですか?


次の3つのテクニックを知っています。


1- Dataプロパティを使用

長所:

  • 追加情報の設定が簡単
  • さらに例外を作成する必要はありません
  • 追加する必要はありません try/catch

短所:

  • に簡単に統合できない Message
  • ロガーはこのフィールドを無視し、ダンプしません
  • キーとキャストの値が必要です object
  • 不変ではない

例:

public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
    foreach (var person in persons)
    {
        try
        {
            person.Abbreviation = GenerateAbbreviation(person.Name);
        }
        catch(Exception ex)
        {
            ex.Data["PersonId"] = person.Id;
            // collect ex
        }
    }
    // throw AggregateException...
}

2-カスタムプロパティを使用する

長所:

  • Dataプロパティに似ていますが、強く型付けされています
  • に統合しやすい Message

短所:

  • カスタム例外が必要
  • ロガーはそれらを無視します
  • 不変ではない

例:

public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
    foreach (var person in persons)
    {
        try
        {
            person.Abbreviation = GenerateAbbreviation(person.Name);
        }
        catch(Exception ex)
        {
            // not suitable for this exception because 
            // it doesn't have anything in common with the Person
        }
    }
    // throw AggregateException...
}

3-例外を別の例外でラップする

長所:

  • Message 予測可能な方法でフォーマットできます
  • ロガーは内部例外をダンプします
  • 不変

短所:

  • 追加が必要 try/catch
  • ネスティングを増やす
  • 例外の深さを増やす

例:

public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
    foreach (var person in persons)
    {
        try
        {
            try
            {
                person.Abbreviation = GenerateAbbreviation(person.Name);
            }
            catch(Exception ex)
            {
                throw new InvalidPersonDataException(person.Id, ex);
            }
        }
        catch(Exception ex)
        {
            // collect ex
        }
    }
    // throw AggregateException...
}

  • 他のパターンはありますか?
  • より良いパターンはありますか?
  • それらのいずれか/すべてのベストプラクティスを提案できますか?

C#の例外には詳しくありませんが、通常、例外がスローされたときにPersonインスタンスがまだ有効であると期待しています。試しましたか?
ジョンクラクリス

1
@JohnKouraklisこれは質問の内容ではありません;-)これは、追加情報が意味することを示すための非常に単純な例です。ここに複数のメソッドが例外をスローできる複数のフレームワーク全体を投稿し、コンテキスト情報の複数のレベルを提供する必要がある場合、誰もおそらくこれを読むことはなく、私はそれを説明するのに本当に苦労しました。
t3chb0t

@JohnKouraklisデモ用に作成しました。
t3chb0t

@ t3chb0tあなたはここであなた自身の質問に答えたと思います。1、2、および3を回答に移動し、私の意見に基づいてスタイルを選択するように求めないように質問を調整することを検討してください。
candied_orange

カスタム例外の何が問題になっていますか?適切に行われれば、それらはドメイン言語の一部であり、実装の詳細から離れた抽象化を実現するのに役立ちます。
ラバーダック

回答:


6

Data FTW

あなたの「コントラ」:

  • 「メッセージに簡単に統合できない」

- >のために、あなたの例外タイプ、オーバーライドする簡単な十分なはずですMessageので、それがあることない組み込んをData場合、私はこれだけを考えるだろうが... Dataメッセージです

  • 「ロガーはこのフィールドを無視し、ダンプしません」

例としてのNlogのグーグルは以下を生成します

例外レイアウトレンダラー

(...)

format-出力のフォーマット。例外のプロパティのコンマ区切りのリストでなければなりません:MessageTypeShortTypeToStringMethodStackTraceData。このパラメーター値は大文字と小文字を区別しません。デフォルト:message

簡単に設定できるようです。

  • 値はオブジェクトであるため、キーとキャストが必要です

え?そこにオブジェクトをダンプして、使用可能なものがあることを確認してくださいToString()メソッド。

また、キーに問題はまったくありません。わずかな一意性を使用するだけで、あなたは大丈夫です。


免責事項:これは、質問からすぐに見ることができData、15分でグーグルで確認したものです。私はそれがやや役立つと思ったので、答えとして出しましたが、私はData自分自身を使ったことがないので、ここの質問者は私よりもこのことをよく知っているかもしれません。


例外には、名前とメッセージという有用な例外が2つしかないという結論に達しました。それ以外はすべて無意味なノイズであり、無視することはできますが、それは単純に脆弱すぎるためです。
t3chb0t

2

なぜ例外を投げるのですか?それらを捕まえて処理するため。

キャッチコードはどのように例外を処理するのですか?Exceptionオブジェクトで定義したプロパティを使用します。

Messageプロパティを使用して例外を特定したり、潜在的なハンドラーが依存すべき「情報」を提供したりしないでください。単純に不安定で信頼性に欠けます。

「データ」プロパティを使用したことはありませんが、非常に一般的に聞こえます。

それぞれが特定の例外なケースを識別する例外の多くのクラスを作成しない限り、「データ」が表すものを例外をキャッチしたとき、どのように知ることができますか?(「メッセージ」に関する前のコメントを参照)。


1
Dataは、処理には役に立たないと思いますが、Message地獄のフォーマットを避けるためにロギングには価値があります。
マーティンBa

-1

3番目の例は気に入っていますが、コードを使用してほとんどの「詐欺」を排除できる別の方法があります。

public IEnumerable<Person> GenerateAbbreviation(IEnumerable<Person> persons)
{
    var exceptions = new List<InvalidPersonDataException>();

    foreach (var person in persons)
    {
        try
        {
            person.Abbreviation = GenerateAbbreviation(person.Name);
        }
        catch(Exception ex)
        {
            exceptions.Add(new InvalidPersonDataException(person.Id, ex));
        }
    }

    if (exceptions.Any())
    {
        throw new AggregateException(exceptions);
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.