DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

デバッガが指定したメソッド内で停止、ステップインしないようにする

デバッグ中にF11キーを押して1行ずつ実行していると、フィールドの値を返すだけのプロパティやメソッドの中までステップインしてしまい、わずらわしく感じることがあります。ここでは、指定したメソッドやプロパティの中にはデバッガが入らない(ステップイン、停止しない)ようにする方法を紹介します。

なお、コード内の特定の範囲でデバッガが停止しないようにする方法は、「デバッグで特定の行が常にステップオーバーされるようにする」で説明しています。

DebuggerHidden属性を使う

DebuggerHiddenAttributeをメソッドに適用すると、そのメソッドはデバッガから隠され、その内ではデバッガが停止しなくなります。

例えば、以下の様なコードがあったとします。

VB.NET
コードを隠すコードを選択
Imports System.Diagnostics

Public Class Program
    'エントリポイント
    Public Shared Sub Main()
        Print1()
        Console.ReadLine()
    End Sub

    'DebuggerHidden属性が適用されたメソッド
    <DebuggerHidden()> _
    Public Shared Sub Print1()
        '↓にブレークポイントがあっても停止しない
        Console.WriteLine("1")
    End Sub
End Class
C#
コードを隠すコードを選択
using System;
using System.Diagnostics;

public class Program
{
    //エントリポイント
    public static void Main()
    {
        Print1();
        Console.ReadLine();
    }

    //DebuggerHidden属性が適用されたメソッド
    [DebuggerHidden()]
    public static void Print1()
    {
        //↓にブレークポイントがあっても停止しない
        Console.WriteLine("1");
    }
}

このコードでは、Print1メソッドにDebuggerHidden属性が適用されています。そのため、Print1メソッド内にブレークポイントがあっても、停止しません。また、Print1メソッドを呼び出す前からF11キーを押してステップ実行してもPrint1メソッド内にはステップインせず、次の行(Console.ReadLine)に移動します。

補足:VB.NETのStopステートメントを使った場合も、ブレークポイントの場合と同様に、DebuggerHidden属性が適用されたメソッド内では停止しません。

属性のあるメソッドから属性のないメソッドを呼び出した時

DebuggerHidden属性が適用されたメソッドから属性が適用されていない別のメソッドを呼び出した場合、属性が適用されていないメソッドにはデバッガが停止します。

例えば以下のように、DebuggerHidden属性が適用されたPrint1メソッドから、属性がないPrint2メソッドを呼び出すとします。

VB.NET
コードを隠すコードを選択
Imports System.Diagnostics

Public Class Program
    'エントリポイント
    Public Shared Sub Main()
        Print1()
        Console.ReadLine()
    End Sub

    'DebuggerHidden属性が適用されたメソッド
    <DebuggerHidden()> _
    Public Shared Sub Print1()
        Console.WriteLine("1")
        Print2()
    End Sub

    Public Shared Sub Print2()
        '↓にブレークポイントがあれば停止する
        Console.WriteLine("2")
    End Sub
End Class
C#
コードを隠すコードを選択
using System;
using System.Diagnostics;

public class Program
{
    //エントリポイント
    public static void Main()
    {
        Print1();
        Console.ReadLine();
    }

    //DebuggerHidden属性が適用されたメソッド
    [DebuggerHidden()]
    public static void Print1()
    {
        Console.WriteLine("1");
        Print2();
    }

    public static void Print2()
    {
        //↓にブレークポイントがあれば停止する
        Console.WriteLine("2");
    }
}

このコードをデバッグすると、Print2内にブレークポイントがあれば停止します。また、Print1メソッドにステップインしようとすると、Print1メソッドにはステップインしませんが、Print2メソッドにはステップインします。

メソッド内で例外がスローされた時

DebuggerHidden属性が適用されたメソッド内で例外がスローされた時、メソッド内では停止しません。メソッドの外で停止します。

例えば以下のようにPrint1メソッドで例外が発生したとします。

VB.NET
コードを隠すコードを選択
Imports System.Diagnostics

Public Class Program
    'エントリポイント
    Public Shared Sub Main()
        Print1()
        Console.ReadLine()
    End Sub

    'DebuggerHidden属性が適用されたメソッド
    <DebuggerHidden()> _
    Public Shared Sub Print1()
        Throw New Exception()
        Console.WriteLine("1")
    End Sub
End Class
C#
コードを隠すコードを選択
using System;
using System.Diagnostics;

public class Program
{
    //エントリポイント
    public static void Main()
    {
        Print1();
        Console.ReadLine();
    }

    //DebuggerHidden属性が適用されたメソッド
    [DebuggerHidden()]
    public static void Print1()
    {
        throw new Exception();
        Console.WriteLine("1");
    }
}

もしPrint1メソッドにDebuggerHidden属性が適用されていなければ、例外がスローされた箇所で停止します。しかし上記のようにDebuggerHidden属性が適用されていれば、Print1メソッド内では停止せず、MainメソッドのPrint1メソッドを呼び出した箇所で停止します。

DebuggerHidden、DebuggerStepThrough、DebuggerNonUserCode属性の違い

DebuggerHiddenAttributeと同じことができる属性に、DebuggerStepThroughAttributeDebuggerNonUserCodeAttributeがあります(DebuggerNonUserCodeAttributeは、.NET Framework 2.0以降)。DebuggerHiddenAttributeの代わりにこれらを使ってもほぼ同じ結果になります。しかしもちろん違いはあります。

補足:VB.NETでは、WindowsフォームアプリケーションのフォームクラスのInitializeComponentメソッドにDebuggerStepThrough属性が適用されています。

デバッガから隠されるか、外部コードとして扱われるか

違いの1つは、デバッガがメソッドを無いものとして扱うか、外部コードとして扱うかです。DebuggerHiddenAttributeの場合は、メソッドはデバッガから隠されます。DebuggerStepThroughとDebuggerNonUserCodeの場合は、外部コードとして扱います。

この違いは、呼び出し履歴(コールスタック)を見れば分かります。DebuggerHiddenAttributeの場合は、メソッドが呼び出し履歴に表示されません。DebuggerStepThroughとDebuggerNonUserCodeの場合は、外部コードとして表示されます。

実際に呼び出し履歴を見てみましょう。上記の2番めのコード(Print1からPrint2を呼び出すコード)で、Print2メソッド内にブレークポイントを挿入し、デバッグを開始します。ブレークポイントで停止したところで、呼び出し履歴を見てみます。もし「呼び出し履歴ウィンドウ」が表示されていないならば、Visual Studioのメニューで[デバッグ]-[ウィンドウ]-[呼び出し履歴]を選択してください。

Print1メソッドにDebuggerHiddenが適用されている時は、以下のようになります。呼び出し履歴にPrint1メソッドは全く表示されません。

DebuggerHidden

Print1メソッドにDebuggerStepThroughかDebuggerNonUserCodeが適用されている時は、以下のようになります。呼び出し履歴でPrint1メソッドは「[外部コード]」と表示されています。

DebuggerStepThrough

[外部コード]となっている部分を表示するには、「呼び出し履歴ウィンドウ」で右クリックして、コンテキストメニューから「外部コードの表示」を選択してください。すると以下のように、この「[外部コード]」が確かにPrint1メソッドであることが分かります。

外部コードの表示

マイコードのみ設定を無効にする

さらに大きな違いは、Visual Studioの設定で「'マイコードのみ'設定を有効にする」を無効にすると現れます。この設定は、Visual Studioのメニューの[ツール]-[オプション](または、[デバッグ]-[オプションと設定])で表示される設定ダイアログの[デバッグ]-[全般]にあります。

デバッグのオプション

「'マイコードのみ'設定を有効にする」を無効にすると、DebuggerStepThrough属性が適用されたメソッド内でもブレークポイントが有効になります。しかしステップインはできません。一方DebuggerNonUserCode属性が適用されたメソッドは、ブレークポイントもステップインも有効になります。しかしDebuggerHidden属性はこの設定による変化がなく、ブレークポイントもステップインも無効です。

適用できるメンバ、型

DebuggerHidden属性は、メソッド、プロパティ、コンストラクタ、インデクサに適用できます。DebuggerStepThrough属性は、メソッド、コンストラクタ、クラス、構造体に適用できます。DebuggerNonUserCode属性は、メソッド、プロパティ、コンストラクタ、インデクサ、クラス、構造体に適用できます。

プロパティにこれらの属性を適用する場合は、GetやSetに適用します。DebuggerStepThrough属性でもこの方法でプロパティに適用できます。

VB.NET
コードを隠すコードを選択
Imports System.Diagnostics

Class SampleClass
    Private _message As String = "こんにちは。"
    Public Property Message() As String
        <DebuggerStepThrough()> _
        Get
            Return Me._message
        End Get
        <DebuggerStepThrough()> _
        Set(ByVal value As String)
            Me._message = value
        End Set
    End Property
End Class
C#
コードを隠すコードを選択
using System;
using System.Diagnostics;

class SampleClass
{
    private string _message = "こんにちは。";
    public string Message
    {
        [DebuggerStepThrough()]
        get { return this._message; }
        [DebuggerStepThrough()]
        set { this._message = value; }
    }
}

DebuggerStepThroughやDebuggerNonUserCode属性はクラスに適用することもできます。クラスに適用すると、そのメンバ全体が影響を受けます。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • .NET Tipsをご利用いただく際は、注意事項をお守りください。