LoginSignup
16
7

More than 1 year has passed since last update.

[.NET] ExceptionのToStringメソッドを活用し、そのオーバーライドを避ける

Last updated at Posted at 2019-07-02

ExceptionクラスのToString()を活用する

ExceptionクラスのToString()は以下を出力する。

  • 例外クラスの名前
  • メッセージの内容
  • 内部例外の内容
  • スタックトレースの内容

このため、発生した例外をログに出力する場合、自分で各プロパティの内容を出力するより、ToString()の内容をそのまま出力したほうが楽だ。

ToString()のオーバーライドを避ける

便利な機能があるということは、その機能を活用する人がいるということだ。
Exceptionクラスの持つ上記の機能を、その派生クラスから取り除いてはならない。

この機能を取り除くという誤りは、「独自プロパティを持たせたカスタム例外」を作成したときによくおきる。
ToString()メソッドに独自プロパティの値を出力する機能を追加したばかりに、内部例外やスタックトレースの内容を出力する機能が取り払われたりする。
クラス名とメッセージだけのログを受け取っても、おそらく、問題の特定はできないだろう。

// EVIL code
public override string ToString()
{
    return $"{GetType()}: {Message}" + Environment.NewLine + $"MyProperty: {MyProperty}";
}

また、この機能を取り払わないよう、自分のToString()から基底クラスの同メソッドをコールし、これに追加する形で独自プロパティの内容を出力しようとしても、理想的な結果にはならない。

// Not good
public override string ToString()
{
    return base.ToString() + Environment.NewLine + $"MyProperty: {MyProperty}";
}

内部例外やスタックトレースの内容のあとに、独自プロパティの内容が出力されることになる。これでは最良の書式とは言えない。

try
{
    // MyPropertyに1, Messageに"this is an example."を指定
    throw new MyException(1, "this is an example.");
}
catch (MyException ex)
{
    Console.WriteLine(ex.ToString());
}

実行結果

Submission#13+MyException: this is an example.
   場所 Submission#15.<<Initialize>>d__0.MoveNext()
MyProperty: 1

この例ではスタックトレースは1行しかないが、10行20行と膨れあがる場合があることを想像してほしい。

オーバーライドすべきはMessageプロパティ

独自プロパティを持つ例外クラスを定義したなら、Messageプロパティの内容をオーバーライドし、そこでプロパティの値を出力しよう。

// Good
public override string Message
{
    get
    {
        return base.Message + Environment.NewLine + $"MyProperty: {MyProperty}";
    }
}

このMessageプロパティがToString()メソッドから参照されるため、独自プロパティの内容が例外クラスの名前と元のメッセージの後、内部例外とスタックトレースの前に出力されるようになる。
書式として前の例よりも良いものになった。もう、ToString()をオーバーライドする必要はない。

try
{
    // MyPropertyに1, Messageに"this is an example."を指定
    throw new MyException(1, "this is an example.");
}
catch (MyException ex)
{
    Console.WriteLine(ex.ToString());
}

実行結果

Submission#9+MyException: this is an example.
MyProperty: 1
   場所 Submission#12.<<Initialize>>d__0.MoveNext()

ここで示したのはあくまで簡略化したコードである。
ほとんどの例外クラスは引数のないコンストラクタを持つので、独自プロパティは値を持たない場合がある。実際のコードでは、この点も考慮に入れて欲しい。

まとめ

  • 例外をログに出力するときは、ExceptionクラスのToString()を活用する。
  • カスタム例外クラスを作成するとき、ToString()のオーバーライドは必要がないかぎり避ける。
  • 独自プロパティの値をToString()の出力に含めるには、Messageプロパティをオーバーライドする。

参考資料

Exceptionクラスやシステム例外系クラスのソースコード
https://referencesource.microsoft.com/

「運用者が困る例外処理の書き方」の@banban525さんのコメント
https://qiita.com/tokishirazu/items/b510ebfdb7ff089fe44c#comment-6415deea1aeefa6b3b13

16
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
7