プリントデバッグ・サバイバルガイド

System.Diagnostics 名前空間Debug クラスや Trace クラスを使ったプ リント・デバッグを行う際に予め知っておくべきことについて書きます。

何故プリント・デバッグなのか

他の人が書いたテストも無く、仕様通りに動いているかどうか*1疑わしいコードに遭遇したことはありませんか?
それを解析もしくはデバッグ、あるいは機能追加する時、何から手をつけていけばいいでしょうか?

また、ステップ実行中には息を潜めているバグが存在しているというのは可能性として十分にありえます。
ここで登場するのが、プリント・デバッグです。
プリント・デバッグというのは、プリント・デバッグ用のコードを注意深く埋め込むことで実行中に何らかのデバッグ用の出力を吐くようにして、バグをとらまえようという試みです。

実際にやってみよう

まずは、Trace クラスや Debug クラスの Listeners プロパティにリスナーを追加する必要があります。
app.config にこんな感じに書いてみてください:

<configuration>
  <system.diagnostics>
    <trace autoflush="true" indentSize="4">
      <listeners>
        <add name="hoge" 
             type="System.Diagnostics.TextWriterTraceListener" 
             initializeData="execution.log" />
      </listeners>
    </trace>
  </system.diagnostics>
</configuration>

これで Trace クラスの Listeners プロパティに TextWriterTraceListener クラスのインスタンスが追加されます。
あと、Debug クラスにも同じことが起こります。

下記は私がよくやる書き方の 1 つです:

void PerformHoge(string parameter) {
    Debug.Print( " ---> {0}#{1}", this.GetType().Name, MethodBase.GetCurrentMehod().Name );
    Debug.Indent();
    
    Debug.WriteLine( parameter, "parameter" );
    
    Debug.Unindent();
    Debug.Print( " <--- {0}#{1}", this.GetType().Name, MethodBase.GetCurrentMehod().Name );
}

主要なメソッドにこんな風に書いておくと、とても捗ることがあるかもしれません。
上記のメソッド PerformHoge() を実行すると、こんな感じのログが出力されるはずです:

---> Hoge#PerformHoge
    parameter: Hello, World!
<--- Hoge#PerformHoge

でも、こんな風に出力すると何が嬉しいんでしょう?
実はメソッドの呼びだされ方がインデントでよく見える様になります。
Indent()Unindent() という 2 つの静的メソッドは、以降に出力されるログのインデントレベルを変更します。
つまり、Indent() はインデントレベルを一段階上げ、Unindent() はその逆のことを行います。

メソッド内のローカル変数やインスタンス変数の値を見たい時というのもあります。
こんな時、私はこうやっています:

Debug.WriteLine( hoge, "hoge" );

これはこんな感じの出力になります:

hoge: Hello, World!

DebugTraceWriteLine() は少々独特です。
この場合の第二引数は何をやっているのでしょう?
第二引数の仮引数名は category と云って実際の値の前の方にコロンを伴って出力されます。
ConsoleWriteLine() と同じシグネチャは存在しますが、引数を 2 つ渡している場合で第二引数が文字列型だった場合、意図したようにはなりません。
この場合のことを考えて、私は string.Format() を使っています。
ここが DebugTrace で厭なところです。

Debug.WriteLine( "Hello, {0}!", "World" );    // => World: Hello, {0}!

ではなく、

Debug.WriteLine( string.Format( "Hello, {0}!", "World" ) );    // => Hello, World!

という風に書きましょう。もしくは Print() を使います:

Debug.Print( "Hello, {0}!", "World" );    // => Hello, World!

おしまい。

参考

*1:仕様書が無かったりする場合もある