web-dev-qa-db-ja.com

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

例外に関する追加情報を提供する必要があるときはいつでも、実際にこれがrightの方法なのかと思います。


この質問のために、私は例を書きました。 Abbreviationプロパティを更新したいクラスがあるとしましょう。 SOLIDの観点からは完全ではないかもしれませんが、worker-methodをいくつかのサービスで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...
}

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

Data[〜#〜] ftw [〜#〜]

あなたの「逆」:

  • 「メッセージに簡単に統合することはできません」

->your例外タイプの場合、Messageをオーバーライドして、doesData .. Dataがメッセージ の場合にのみ、これを検討します。

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

例としてのNlogのグーグル yields

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

(...)

format-出力のフォーマット。例外プロパティのカンマ区切りリストである必要があります:MessageTypeShortTypeToStringMethodStackTraceData。このパラメーター値では、大文字と小文字は区別されません。デフォルト:message

だからそれは簡単に設定できるようです。

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

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

また、キーに問題はありません。ちょっと穏やかな個性を使うだけでいいです。


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

6
Martin Ba

なぜ例外をスローするのですか?彼らを捕まえて取り扱ってもらうため。

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

Messageプロパティを使用して例外を特定したり、潜在的なハンドラーが依存すべき「情報」を提供したりしないでください。それは単に揮発性が高すぎて信頼性が低い。

これまで「Data」プロパティを使用したことはありませんが、あまりにも一般的に聞こえます。

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

2
Phill W.