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

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

![無題.png](https://qiita-image-store.s3.amazonaws.com/0/135472/05e810fe-b535-390e-e4ec-5c82699e100e.png)

#拡張機能導入後

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

![無題.png](https://qiita-image-store.s3.amazonaws.com/0/135472/87cb1d02-1e8b-078b-a559-023f7a08752f.png)

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

![無題.png](https://qiita-image-store.s3.amazonaws.com/0/135472/852eb61a-988b-0e8f-826f-22b5091e2f48.png)

#拡張機能の作り方

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

##準備

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

##本体

```csharp: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にアセンブリ情報を追記する。デバッガから見てどのようにメソッドを呼び出すかを知らせるため。

```csharp: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]__属性が付与されていないとうまくビジュアライザで表示できない。

```csharp: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]__属性を付与していないクラスでも動作可能となる。

```csharp:本体部分

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](https://github.com/msgpack/msgpack-cli)というパッケージを使用した。デシリアライズの際に、データグリッドビューで扱いやすいようにDictionary\<string, ocbect>[]に変換して、ウィンドウズフォームに渡している。

```csharp: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の項目を参考にした