Help us understand the problem. What is going on with this article?

Visual Studioでデバッグ中にList<T>を表形式で表示する拡張機能の作成

More than 1 year has passed since last update.

はじめに

 Visual Studioで作業をしていてデバッグ中にList<T>の中身を一覧表で確認したかった。
 初期状態では、この画像のようにデバッグ時にList<T>の中身を確認するのは結構大変。リストの要素一つ一つをクリックする必要がある。

無題.png

拡張機能導入後

 マウスオーバーした時にList<T>オブジェクトに虫眼鏡アイコンが出るようになった。

無題.png

 虫眼鏡アイコンをクリックすると、一覧表が出現する。データの確認が楽に行えるようになった。

無題.png

拡張機能の作り方

 Visual Studio 2017で作成した。
 用いた言語はC#。

準備

  • プロジェクト新規作成からクラスライブラリ作成を選択
  • プロジェクトの参照設定にMicrosoft.VisualStudio.DebuggerVisualizersを追加

本体

ListEntityVisualizer.cs
using Microsoft.VisualStudio.DebuggerVisualizers;
using System.Windows.Forms;

namespace EntityVisualizer
{
    public class ListEntityVisualizer : DialogDebuggerVisualizer
    {
        protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
        {
            dynamic entity = objectProvider.GetObject();

            using (var dialog = new EntityDialog(entity))
            {
                dialog.StartPosition = FormStartPosition.CenterParent;
                dialog.ShowDialog();
            }
        }
    }
}

 デバッグ中に虫眼鏡ボタンを押したときに表示されるツールはビジュアライザと呼ばれている。独自のビジュアライザを作成するにはDialogDebuggerVisualizerクラスを継承し、Showメソッドをオーバーライドする。

 デバッガから送られてくるList<T>のオブジェクトはobjectProvider.GetObject()で取得できる。

 EntityDialogは自作したウィンドウズフォームクラスで、データグリッドビューと終了ボタンを持つ。データグリッドビューにはコンストラクタで受け取ったList<T>の内容が表示される。目新しいこともないのでソースコードは省略。

アセンブリ情報の追記

 プロジェクトのAssemblyInfo.csにアセンブリ情報を追記する。デバッガから見てどのようにメソッドを呼び出すかを知らせるため。

AssemblyInfo.cs
using EntityVisualizer;
using Microsoft.VisualStudio.DebuggerVisualizers;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;

[assembly: System.Diagnostics.DebuggerVisualizer(typeof(ListEntityVisualizer), typeof(VisualizerObjectSource), Target = typeof(List<>), Description = "エンティティ ビジュアライザ")]

 今回はList<T>の情報を確認したいのでTarget = typeof(List<>)としている。
 Descriptionはデバッグ時に表示されるビジュアライザの名前。

拡張機能の導入方法

 ビルドして得られたdllを以下のどちらかのフォルダに配置する。

  • (VisualStudioInstallPath)\Common7\Packages\Debugger\Visualizers
  • My Documents\(VisualStudioVersion)\Visualizers

デバッグ時の注意点

 デバッガ、ビジュアライザ間のデータ受け渡しにおいてシリアライズが行われるため、受け渡されるクラスに[Serializable]属性が付与されていないとうまくビジュアライザで表示できない。

IceCream.cs
using System;

namespace EntityVisualizerTest.Entity
{
    [Serializable]
    public class IceCream
    {
        public string ID { get; set; }
        public string Flavor { get; set; }
        public int Price { get; set; }
        public int Type { get; set; }
    }
}

 ビジュアライザを用いたい場合はこのようにシリアライズ可能にしておく。

[Serializable]属性を付与せずに動作させる場合 ※2017年5月27日追記

 シリアライズを行っているメソッド(VisualizerObjectSource.GetData)をオーバーライドすることで、[Serializable]属性を付与していないクラスでも動作可能となる。

本体部分
using Microsoft.VisualStudio.DebuggerVisualizers;
using MsgPack;
using MsgPack.Serialization;
using System.IO;
using System.Linq;
using System.Windows.Forms;

namespace EntityVisualizer
{
    public class ListEntityVisualizerObjectSource : VisualizerObjectSource
    {
        /// <summary>
        /// シリアライズを行なう
        /// </summary>
        public override void GetData(object target, Stream outgoingData)
        {
            var context = new SerializationContext();
            context.SerializationMethod = SerializationMethod.Map;

            var serializer = MessagePackSerializer.Get(target.GetType(), context);

            serializer.Pack(outgoingData, target);
            outgoingData.Position = 0;
        }
    }

    public class ListEntityVisualizer : DialogDebuggerVisualizer
    {
        protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
        {
            // Dictionary<string, ocbect>[]へのデシリアライズ
            var rawObject = Unpacking.UnpackObject(objectProvider.GetData());
            var entity = rawObject
                .AsList()
                .Select(x => x.AsDictionary())
                .Select(x => x.ToDictionary(y => y.Key.ToString(), y => y.Value.ToObject()))
                .ToArray();

            using (var dialog = new EntityDialog(entity))
            {
                dialog.StartPosition = FormStartPosition.CenterParent;
                dialog.ShowDialog();
            }
        }
    }
}

 シリアライズ、デシリアライズにはmsgpack-cliというパッケージを使用した。デシリアライズの際に、データグリッドビューで扱いやすいようにDictionary<string, ocbect>[]に変換して、ウィンドウズフォームに渡している。

AssemblyInfo.cs
using EntityVisualizer;
using Microsoft.VisualStudio.DebuggerVisualizers;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;

[assembly: DebuggerVisualizer(typeof(ListEntityVisualizer), typeof(ListEntityVisualizerObjectSource), Target = typeof(List<>), Description = "エンティティ ビジュアライザ")]

 アセンブリ情報もこのように自作クラス2つを記述しておく(ListEntityVisualizer, ListEntityVisualizerObjectSource)。

さいごに

ソースコード置き場
https://github.com/ttlatex/EntityVisualizer

参考サイト

MSDN ビジュアライザーを記述する
https://msdn.microsoft.com/ja-jp/library/e2zc529c.aspx

msgpack-cli wiki
https://github.com/msgpack/msgpack-cli/wiki
とくにHandling Dynamic Objectの項目を参考にした

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away