式木を使った簡単なPrintデバッグ出力

# 概要
アプリケーションを実行中に変数の値を確認したい際には
`Console.WriteLine()`や`Debug.WriteLine()`が最も使われると思います。
一般的にはPrintデバッグと言われていますね(C言語のprintから？）。

私はこの際に下記コードのように`変数名 = 変数値`の文字列に整形しています。

```csharp
Console.WriteLine($"{nameof(pTaro)} = {pTaro}");
```

これを毎回書くのがダルくなってきたので、式木を使って下記コードのように簡単にする方法を紹介します。

```csharp
MyReport.ConsoleOut(() => pTaro);
```

# 変更前
今回はConsoleアプリケーションを使用しますので、`Console.WriteLine()`を使用します。
WPF等の場合や、VisualStudioの出力ウインドウを使用したい場合は`Debug.WriteLine()`を使用します。

まず出力されるクラスとして`Person`クラスを作っておきます。
この時`ToString()`をオーバーライドしておくと、デバッグが捗ります。

```csharp
class Person
{
    public string Name { get; set; }
    public override string ToString() => Name;
}
```

次にPersonクラスを使用してこんなサンプルプログラムを作ります。

```csharp
class Program
{
    static void Main(string[] args)
    {
        var pTaro = new Person { Name = "太郎" };
        var pHithoshi = new Person { Name = "均" };
        Console.WriteLine($"{nameof(pTaro)} = {pTaro}");
        Console.WriteLine($"{nameof(pHithoshi)} = {pHithoshi}");

        Console.WriteLine("===太郎は進化しました===");
        pTaro.Name = "NEOTARO";

        Console.WriteLine($"{nameof(pTaro)} = {pTaro}");
        Console.WriteLine($"{nameof(pHithoshi)} = {pHithoshi}");

        Console.ReadLine();
    }
}
```

このプログラムの出力結果はこんな感じです。

```:Console出力結果
pTaro = 太郎
pHithoshi = 均
===太郎は進化しました===
pTaro = NEOTARO
pHithoshi = 均
```

# めんどうなところ
`Console.WriteLine($"{nameof(pTaro)} = {pTaro}");`
とか毎回書くのがダルいです。特に`nameof`とかが。
そこでこの部分だけメソッドに抽出して`pTaro`だけ引数として渡そうとしても、今度は`nameof`の結果が意図通りになりません。

```csharp:失敗メソッド
static void ReportFail(Person person)
    => Console.WriteLine($"{nameof(person)} = {person}");
```

```:Console出力結果
person = 太郎
```

# 解決策

そこで式木を使用したメソッドでこの問題を解決します。

```csharp
static class MyReport
{
    /// <summary>
    /// ConsoleにReport出力
    /// </summary>
    public static void ConsoleOut(Expression<Func<object>> exp)
        => Console.WriteLine(CreateReportText(exp));

    /// <summary>
    /// DebugにReport出力
    /// </summary>
    public static void DebugOut(Expression<Func<object>> exp)
        => Debug.WriteLine(CreateReportText(exp));

    /// <summary>
    /// 式木を使用してReport用文字列に変換
    /// </summary>
    /// <param name="exp">Report対象の式木 ex. ()=>MyAge</param>
    /// <returns>"MyAge = 17"</returns>
    public static string CreateReportText(Expression<Func<object>> exp)
    {
        var memName = (exp.Body as MemberExpression)?.Member.Name;
        var result = exp.Compile().Invoke();
        return $"{memName} = {result}";
    }
}
```

出力したい変数やプロパティを出力するデリゲートを式木で受け取ります。
一番簡単なのはラムダ式で`()=>変数`と書くことです。

`CreateReportText`内部では受け取った式木を分解して変数名を取り出しています。
その後式木をコンパイルしてデリゲートに変換し、即実行して変数の値を取得します。
そして両者を文字列として整形しています。

# 変更後
使用するプログラムを変更します。

```csharp
class Program
{
    static void Main(string[] args)
    {
        var pTaro = new Person { Name = "太郎" };
        var pHithoshi = new Person { Name = "均" };

        MyReport.ConsoleOut(() => pTaro);
        MyReport.ConsoleOut(() => pHithoshi);
        Console.WriteLine("===太郎は進化しました===");
        pTaro.Name = "NEOTARO";
        MyReport.ConsoleOut(() => pTaro);
        MyReport.ConsoleOut(() => pHithoshi);

        Console.ReadLine();
    }
}
```

見た目の長さはあまり変わりませんが、インテリセンスを使用した書きやすさは段違いです。
出力結果は同じです。

# 問題点
Reportのたびに内部で式木からデリゲートを生成しているので重いです。
パフォーマンスがネックになるのであれば、内部でデリゲートをキャッシュする必要があります。

# 参考
[[C#][ラムダ式][式木] Expression を使ってラムダ式のメンバー名を取得する (プログラミング C# - 翔ソフトウェア (Sho's))](http://blog.shos.info/archives/2012/12/cexpression_expression_2.html)

# 環境
VisualStudio2017
.NET Framework 4.7