Visual Studio と T4 (Text Template Transformation Toolkit) を使用してコードを自動生成する

ページ作成日 :

動作確認環境

Visual Studio
  • Visual Studio 2022
.NET
  • .NET 8.0
Windows
  • Windows 11

動作必須環境

Visual Studio
  • ある程度古いバージョンでも動きます
.NET
  • ある程度古いバージョンでも動きます

はじめに

通常コードを作る場合は手作業で文字を打ち込んでコードを作ると思いますが、特定の法則で作る必要があるコードやデータをもとにコードを作る場合は自動生成ができると便利です。 ここでは T4 (Text Template Transformation Toolkit) を使用してコードを自動生成する方法について説明します。

T4 はスクリプト形式なので Visual Studio 上であれば T4 ファイルを作った後にすぐにコード生成ができるので非常に手軽です。 コードの自動生成と書いていますが生成されるものはただのテキストなので、プログラム以外にも XML や JSON などテキスト形式であれば何でも生成することが可能です。

本 Tips では実際に T4 でコードを自動生成するまでの手順を説明することが主な目的であるため T4 の詳細については深くは説明はしませんが、 C# でプログラムを作ったことがある方であればすぐに理解できると思います。 もっと詳しく T4 について知りたい方は公式サイトや他サイトなどで調べてみて下さい。

前提条件

本 Tips は以下を前提として説明しています。

  • Visual Studio が Windows にインストールされていること
  • C# を理解していること

T4 は一部のプロジェクトを除きほとんどのプロジェクトで使用できるのでインストール時のワークロードの選択も基本的に自由です。

プロジェクトの作成

前述の通り T4 は一部のプロジェクトを除きほとんどのプロジェクトで使用できるのでどの種類のプロジェクトを作成しても問題ありません。 今回はコンソール アプリのプロジェクトを作成しています。

T4 ファイル (.tt) の作成

プロジェクトファイルやフォルダを右クリックして新しい項目を追加します。

「すべてのテンプレートの表示」ボタンをクリックします。

左のツリーから「全般」を選択し中央の一覧から「テキスト テンプレート」を選択します。ファイル名は任意です。

ファイルを作成した直後以下のようなダイアログが表示される場合があります。 これは T4 ファイルでコードの自動生成が実行されるタイミングで表示されます。 T4 ファイルはファイルを保存したり表示を切り替えるたびに自動生成処理が実行されます。 そのまま自動生成処理を行う場合は「OK」ボタンをクリックしてください。 毎回ダイアログが表示されるのが面倒な場合は「今後このメッセージを表示しない」にチェックを入れてください。 ダイアログの文言の通り T4 ファイルに不正な処理が記述されているとその通りに実行されるので注意してください。

ファイルを追加するとソリューション エクスプローラーに .tt ファイルが追加されていることを確認できます。

ファイルの中身は以下のようになっています。

ソリューション エクスプローラーで .tt ファイルを展開すると同名のテキストファイルが作成されています。 これが自動生成されたファイルになります。まだ何も記述していないのでファイルの中身は空です。

自動生成されるファイルを .cs ファイルにする

T4 を使う場合、大抵はプログラムのコードを自動生成するので拡張子は初期状態の .txt ではなく .cs にしたいはずです。 その場合は .tt ファイルを開き outputextension.cs にして保存してください。

すると自動生成されるファイルの拡張子が .cs になるはずです。ファイル名自体は .tt ファイルと同じになるので変えたい場合は .tt ファイルの名前を変えてください。

とりあえずなんか書いて自動生成されるファイルに出力する

T4 では基本的に書いた内容がそのまま出力されるようになっています。 <# ... #> で囲まれている内容が T4 の処理であり、それ以外は実際に出力されるテキストとなります。

例えば以下のように .tt ファイルに記述してみます。

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>

public static class Sample
{
  public static void Hello()
  {
    Console.WriteLine("Hello T4!!");
  }
}

保存すると自動生成されたファイルのほうは書いたとおりに出力されるはずです。

出力されているものはコードなのでもちろん Program.cs からも呼び出すことができます。

コードを自動生成してみる

前述の通り書いたものはそのまま出力されることは確認しましたが、これでは普通にコードを書いたものとなんら変わりません。 では実際に T4 のスクリプトを使用してコードを自動生成してみます。 なお作り方は多種多様なのでここでは簡単な説明のみにし、後は作りたいように作り替えていってください。

今回は例として「string に各々の型の Parse メソッドを追加し、変換できなかったら指定したデフォルト値を返す ParseXXXX メソッド」を作成してみます。 今回はあくまでも作成例なので不足と感じる部分があれば追記してください。

細かい部分を説明しなくても C# を理解できる人なら見たほうが早いのでまずは全コード掲載します。

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>

<#
  List<string> types = new(){"Int", "Short", "Long", "Float", "Double", "Decimal"}; 
#>

public static class ParseExtensions
{
<# foreach (var type in types) { #>
<#   var typeLower = type.ToLower(); #>
  public static <#= typeLower #> Parse<#= type #>(this string self, <#= typeLower #> defaultValue)
  {
    return <#= typeLower #>.TryParse(self, out var val) ? val : defaultValue;
  }
<# } #>
}

やってることは最初に作る分の型を List で定義しておいてそれらを foreach で回してその数だけメソッドを生成しています。 T4 のスクリプト部分は C# で記述するので C# がわかるのであれば理解は可能かと思います。

これを実行すると以下のようなコードが生成されます。

public static class ParseExtensions
{
  public static int ParseInt(this string self, int defaultValue)
  {
    return int.TryParse(self, out var val) ? val : defaultValue;
  }
  public static short ParseShort(this string self, short defaultValue)
  {
    return short.TryParse(self, out var val) ? val : defaultValue;
  }
  public static long ParseLong(this string self, long defaultValue)
  {
    return long.TryParse(self, out var val) ? val : defaultValue;
  }
  public static float ParseFloat(this string self, float defaultValue)
  {
    return float.TryParse(self, out var val) ? val : defaultValue;
  }
  public static double ParseDouble(this string self, double defaultValue)
  {
    return double.TryParse(self, out var val) ? val : defaultValue;
  }
  public static decimal ParseDecimal(this string self, decimal defaultValue)
  {
    return decimal.TryParse(self, out var val) ? val : defaultValue;
  }
}

生成されるのはコードなのでそのままプログラムで使用することができます。

T4 の詳細を少しだけ説明

T4 のスクリプト部分は C# で作れるといっても T4 の C# と実際に生成される C# のコードの区分けは必要です。 その区分けをしているのが <# .... #> の部分です。 <# .... #> の中に書かれているコードが T4 のスクリプトの部分で実際に出力されるコードではなくスクリプトとして実行されるコードになります。

<# .... #> の中身自体は C# そのままなので説明しませんが <# .... #> の枠の部分はいくつか種類があります。 それぞれ以下のような使い分けがあります。

コード 説明
<#@ .... #> 主に各種ヘッダーの宣言で使用します。T4 のコードの最初のほうで assemblyimport の宣言で使用します。
<# .... #> T4 で処理されるコードを記述します。複数行に分けることができます。この範囲に記述されたものは処理として動作するだけであり出力テキストには影響しません。
<#= .... #> 変数などの値を出力結果として出力したい場合に使用します。例えば string text = "Sample"; という変数を <#= text #> と記述すれば Sample が出力されます。
<#+ .... #> クラスやメソッドを定義する場合に使用します。基本的には T4 ファイルの後ろのほうに記述します。

T4 のエディタについて

Visual Studio に拡張機能を入れていない場合 .tt ファイルを開くと色付きのない白と黒のテキストで表示され割と見づらいです。

.tt ファイル用の拡張機能を入れると見やすく表示してくれたりするものもあるので好きなものを探してみてください。 有志によって作られているものなので時期や Visual Studio のバージョンによって内容が変わる場合があります。 拡張機能は Visual Studio のメニューの「拡張機能」から追加可能です。

以下は Visual Studio 2022 で「T4 Language」という拡張機能を入れたときのものです。