概要
アプリケーションを実行中に変数の値を確認したい際には
Console.WriteLine()
やDebug.WriteLine()
が最も使われると思います。
一般的にはPrintデバッグと言われていますね(C言語のprintから?)。
私はこの際に下記コードのように変数名 = 変数値
の文字列に整形しています。
Console.WriteLine($"{nameof(pTaro)} = {pTaro}");
これを毎回書くのがダルくなってきたので、式木を使って下記コードのように簡単にする方法を紹介します。
MyReport.ConsoleOut(() => pTaro);
変更前
今回はConsoleアプリケーションを使用しますので、Console.WriteLine()
を使用します。
WPF等の場合や、VisualStudioの出力ウインドウを使用したい場合はDebug.WriteLine()
を使用します。
まず出力されるクラスとしてPerson
クラスを作っておきます。
この時ToString()
をオーバーライドしておくと、デバッグが捗ります。
class Person
{
public string Name { get; set; }
public override string ToString() => Name;
}
次にPersonクラスを使用してこんなサンプルプログラムを作ります。
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();
}
}
このプログラムの出力結果はこんな感じです。
pTaro = 太郎
pHithoshi = 均
===太郎は進化しました===
pTaro = NEOTARO
pHithoshi = 均
めんどうなところ
Console.WriteLine($"{nameof(pTaro)} = {pTaro}");
とか毎回書くのがダルいです。特にnameof
とかが。
そこでこの部分だけメソッドに抽出してpTaro
だけ引数として渡そうとしても、今度はnameof
の結果が意図通りになりません。
static void ReportFail(Person person)
=> Console.WriteLine($"{nameof(person)} = {person}");
person = 太郎
解決策
そこで式木を使用したメソッドでこの問題を解決します。
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
内部では受け取った式木を分解して変数名を取り出しています。
その後式木をコンパイルしてデリゲートに変換し、即実行して変数の値を取得します。
そして両者を文字列として整形しています。
変更後
使用するプログラムを変更します。
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))
環境
VisualStudio2017
.NET Framework 4.7