あたも技術ブログ

セットジャパンコーポレーションの社員が運営しています。

【C#】デバッグ時に実行されるメソッドのトレースを出力する

 今回はC#デバッグ時に実行のトレースを行う方法を紹介します。用途としては非同期の場合にどこから呼出しされているか調査したいときなどに利用できるかと思います。
 方法としては2通りでCallerMemberNameAttributeクラスを利用する方法と、StackFrameクラスを利用する方法があります。

 以下はフォームにボタンのみを配置し、ボタンを押下すると出力ウィンドウへトレースの結果が出力されるサンプルとなっています。

デザイナー側実装

namespace DebugSample
{
  partial class Form1
  {
    /// <summary>
    /// 必要なデザイナー変数です。
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary>
    /// 使用中のリソースをすべてクリーンアップします。
    /// </summary>
    /// <param name="disposing">マネージ リソースが破棄される場合 true、破棄されない場合は false です。</param>
    protected override void Dispose(bool disposing)
    {
      if (disposing && (components != null))
      {
        components.Dispose();
      }
      base.Dispose(disposing);
    }

    #region Windows フォーム デザイナーで生成されたコード

    /// <summary>
    /// デザイナー サポートに必要なメソッドです。このメソッドの内容を
    /// コード エディターで変更しないでください。
    /// </summary>
    private void InitializeComponent()
    {
      this.button1 = new System.Windows.Forms.Button();
      this.SuspendLayout();
      // 
      // button1
      // 
      this.button1.Location = new System.Drawing.Point(84, 64);
      this.button1.Name = "button1";
      this.button1.Size = new System.Drawing.Size(75, 23);
      this.button1.TabIndex = 0;
      this.button1.Text = "出力";
      this.button1.UseVisualStyleBackColor = true;
      this.button1.Click += new System.EventHandler(this.button1_Click);
      // 
      // Form1
      // 
      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
      this.ClientSize = new System.Drawing.Size(255, 146);
      this.Controls.Add(this.button1);
      this.Name = "Form1";
      this.Text = "Form1";
      this.ResumeLayout(false);

    }

    #endregion

    private System.Windows.Forms.Button button1;
  }
}


 実行サンプル用にボタンが配置してあるだけのウィンドウズフォームです。

コードビハインド側実装

using System;
using System.Windows.Forms;

namespace DebugSample
{
  /// <summary>
  /// サンプル用フォーム
  /// </summary>
  public partial class Form1 : Form
  {
    /// <summary>
    /// クラスの新しいインスタンスを生成します。
    /// </summary>
    public Form1()
    {
      InitializeComponent();
    }

    /// <summary>
    /// button1がクリックされたときに発生します。
    /// </summary>
    /// <param name="sender">呼び出し元オブジェクト</param>
    /// <param name="e">イベント引数</param>
    private void button1_Click(object sender, EventArgs e)
    {
      DebugUtil.DebugTrace("出力テスト");

      Console.WriteLine("");

      DebugUtil.DebugTrace(5);
    }
  }
}


 実行のトレース出力を行うためのユーティリティクラスです。

デバッグ用のユーティリティクラス実装

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;

namespace DebugSample
{
  /// <summary>
  /// デバッグ用のユーティリティクラス
  /// </summary>
  public static class DebugUtil
  {
    /// <summary>
    /// 呼び出し元メンバーを出力します。
    /// </summary>
    /// <param name="message">デバッグ出力するメッセージ</param>
    /// <param name="member">呼び出し元のメンバー</param>
    /// <param name="filePath">呼び出し元のファイルパス</param>
    /// <param name="line">呼び出し元の行番号</param>
    public static void DebugTrace(string message = "", [CallerFilePath] string sourceFile = "", [CallerLineNumber] int line = 0, [CallerMemberName] string member = "")
    {
      if (!String.IsNullOrEmpty(message)) { Console.WriteLine(String.Format("Message => {0} ", message)); }
      Console.WriteLine(String.Format("Source File Name => {0}", Path.GetFileName(sourceFile)));
      Console.WriteLine(String.Format("Source Directory Path => {0}", Path.GetDirectoryName(sourceFile)));
      Console.WriteLine(String.Format("Method Name => {0}", member));
      Console.WriteLine(String.Format("Line Number => {0}", line));
    }

    /// <summary>
    /// 指定したフレーム数の呼び出し元メンバーを出力します。
    /// </summary>
    /// <param name="outputLevel">スキップするスタック上のフレーム数</param>
    public static void DebugTrace(int skipFrames)
    {
      // フレーム数が0より小さい場合、処理を行わない
      if (skipFrames < 0) return;

      // フレームが1つ前になるまで出力する
      for (int i = skipFrames; 1 <= i; i--) { FrameTrace(i); }
    }

    /// <summary>
    /// 指定したフレーム数の呼び出し元メンバーを出力します。
    /// </summary>
    /// <param name="outputLevel">スキップするスタック上のフレーム数</param>
    private static void FrameTrace(int skipFrames)
    {
      var frame = new StackFrame(skipFrames);
      var method = frame.GetMethod();
      Console.WriteLine(String.Format("Method Name => {0}", method.ReflectedType.FullName + "." + method.Name));
    }
  }
}


 以下は出力結果です。

Message => 出力テスト 
Source File Name => Form1.cs
Source Directory Path => c:\Users\xxxxxx\Documents\Projects\DebugSample\DebugSample
Method Name => button1_Click
Line Number => 26

Method Name => System.Windows.Forms.Button.OnMouseUp
Method Name => System.Windows.Forms.Button.OnClick
Method Name => System.Windows.Forms.Control.OnClick
Method Name => DebugSample.Form1.button1_Click
Method Name => DebugSample.DebugUtil.DebugTrace

 ユーティリティクラスのDebugTraceメソッドでメッセージ引数を持つメソッドの場合、行番号までが出力されます。
 DebugTraceメソッドでフレーム数引数を持つメソッドの場合、名前空間.クラス.メソッド名で出力されます。