読者です 読者をやめる 読者になる 読者になる

覚えておきたい DebuggerDisplay

皆もうとっくに知ってるかもしれませんが、最近 DebuggerDisplay 属性というのを覚えました。

https://msdn.microsoft.com/ja-jp/library/x810d419.aspx

こいつがなかなかに良いやつだったので、ちょっと調べた結果をメモっておきます。

目的と基本的な使い方

DebuggerDisplay 属性は、Visual Studio でのデバッグ時にインスタンスの値を表示するのに使います。まぁ例を見た方が早いです。

class Program
{
    static void Main(string[] args)
    {
        var obj = new Employee { Id = 1, Name = "太郎" };
        Console.WriteLine(obj);
    }
}
[DebuggerDisplay("Id={Id}, Name={Name,nq}")]
public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
}

このコードの Console.WriteLine(obj);ブレークポイントを張って、Visual Studioデバッグ実行します。

ブレークしたら、[デバッグ] > [ウインドウ] > [ローカル] でローカル変数のキャプチャを見てみると、、、

f:id:kendik:20150415005021p:plain

この通り、 DebuggerDisplay に設定した値が参照できます。

メソッド呼び出しも出来る

先ほど DebuggerDisplay に設定したのはクラスのプロパティでした。
へーじゃあもしかしてメソッドも呼びだしとかも出来るの?という疑問も出てくるかと思いますが、出来ます。

class Program
{
    static void Main(string[] args)
    {
        var obj = new Employee { Id = 1, Name = "太郎" };
        Console.WriteLine(obj);
    }
}
[DebuggerDisplay("{Debug()}")]
public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }

    public string Debug()
    {
        return "called Debug()";
    }
}

f:id:kendik:20150415005707p:plain

夢が広がりますね。

static だって private だって見えちゃう

で、プロパティも見えます、メソッドも呼べますときたら今後は可視性です。どこまで見えるか?全部見えます。

class Program
{
    static void Main(string[] args)
    {
        var obj = new Employee { Id = 1, Name = "太郎" };
        Console.WriteLine(obj);
    }
}
[DebuggerDisplay("{PrivateStaticDebug()}")]
public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }

    private static string PrivateStaticDebug()
    {
        return "called PrivateStaticDebug()";
    }
}

この通り。

f:id:kendik:20150415005736p:plain

DebuggerDisplay 属性が付いてないクラスも見えたりするの?します。

class Program
{
    static void Main(string[] args)
    {
        var obj = new Employee();
        Console.WriteLine(obj);
    }
}
[DebuggerDisplay("{PrivateStaticDebug()}")]
public class Employee
{
    private PrivateClass _field = new PrivateClass();
}
public class PrivateClass
{
    private string _privateField = "private field";
}

f:id:kendik:20150415005801p:plain

やばいですね。やりたい放題です。

注意点

そんな便利な DebuggerDisplay 属性ですが、ちょっと注意点があります。
MSDN を読むと分かりますが、メソッド呼び出しは推奨されていません。なんでかというと、ステップ実行毎に呼び出されるためです。

class Program
{
    static void Main(string[] args)
    {
        var obj = new Employee();
        Console.WriteLine(obj);
        Console.WriteLine(obj);
    }
}
[DebuggerDisplay("{Debug()}")]
public class Employee
{
    private int count = 0;
    public string Debug()
    {
        return "called Debug() " + (++count);
    }
}

↑のコードで最初の Console.WriteLine(obj); 時点では

f:id:kendik:20150415005815p:plain

なんですが、次のステップに進むと、、、

f:id:kendik:20150415005831p:plain

count が上がりました。再度のメソッド呼び出しがかかってますね。
なので、あまり重いメソッドを呼び出したりすると、ステップ実行がしんどくなります。ただ、デバッグ実行したら常に呼び出されるか?例えば 100 ステップのコードであれば 100 回呼び出されるかというとそうではなくて、ステップ実行しなければ大丈夫です。

まとめ

知っている人も多いかと思いますが、デバッグ実行時に表示される値は ToString() をオーバーライドしている場合その呼び出し結果が使われます。なので、 DebuggerDisplay 属性をそんな使うかというと、実際使わないですね。

ただ、ToString() では困るケースというのが全くない訳でもなくて、例えば ToString() は別にデバッグ用ではないので、デバッグには足りない実装が既にされていたりします。あるいは、private も参照できるということで、そういう特殊な状況において輝いてくる機能かなと思います。なので、普段使いとはいかなくとも覚えておいて損はないかと。