T4 テキスト テンプレートの記述に関するガイドライン
更新 : 2011 年 3 月
ここでは、Visual Studio でプログラム コードまたは他のアプリケーション リソースを生成する場合に役立つ一般的なガイドラインを示します。 絶対的な規則ではありません。
デザイン時 T4 テンプレートに関するガイドライン
デザイン時 T4 テンプレートは、デザイン時に Visual Studio プロジェクトのコードを生成するテンプレートです。 詳細については、「T4 テキスト テンプレートを使用したデザイン時コード生成」を参照してください。
アプリケーションの中で可変性のある部分を生成の対象にする。
アプリケーションの中でコード生成に最も適しているのは、プロジェクト中に変更になる可能性のある部分や、アプリケーションの各バージョン間で異なる部分です。 このように可変性のある部分を不変的な部分から切り離すことで、生成の対象が見極めやすくなります。 たとえば、Web サイトを提供するアプリケーションの場合、標準的なページ提供機能と、ページ間のナビゲーション パスを定義するロジックとを分離します。可変性のある部分をソース モデルにまとめる。
モデルはファイルまたはデータベースであり、それぞれのテンプレートは、生成の対象となるコードの可変部分について、このファイルまたはデータベースを読み取ることによって特定の値を取得します。 モデルには、データベース、独自設計の XML ファイル、ダイアグラム、またはドメイン固有言語を使用できます。 通常は、1 つの Visual Studio プロジェクトで 1 つのモデルを使用して複数のファイルを生成します。ファイルはそれぞれ別のテンプレートから生成されます。1 つのプロジェクトで複数のモデルを使用することができます。 たとえば、Web ページ間のナビゲーション用のモデルと、ページのレイアウト用のモデルとを別個に定義することができます。
モデルでは、実装ではなくユーザーのニーズやボキャブラリに重点を置く。
たとえば、Web サイト アプリケーションでは、モデルで Web ページとハイパーリンクを参照することが求められます。モデルが表す情報の種類に合った表示形式を選択するのが理想的です。 たとえば、Web サイトを介したナビゲーション パスのモデルであれば、四角形や矢印の図を使用できます。
生成されたコードをテストする。
手動テストまたは自動テストを使用して、生成されたコードがユーザーの要求どおりに動作することを確認します。 コードの生成元となったモデルを使用してテストを生成しないようにします。場合によっては、一般的なテストをモデルに対して直接実行します。 たとえば、Web サイト内のすべてのページに対して他のサイトから移動できるかどうかのテストを作成します。
カスタム コードで部分クラスを生成できるようにする。
生成されたコードだけでなく、手動でもコードを記述できるようにします。 今後必要になるであろうすべてのバリエーションをコード生成だけでカバーできることは、まずありません。 したがって、生成されたコードの一部をオーバーライドするか独自にコードを追加するような余地を残しておく必要があります。 生成される成果物が .NET 言語 (Visual C#、Visual Basic など) であれば、特に次の 2 点が鍵となります。部分クラスとなるように生成する。 これにより、生成されたコードに自由にコンテンツを追加することができます。
基本クラスと派生クラスの対で生成する。 生成されるメソッドとプロパティはすべて基本クラスに含め、派生クラスにはコンストラクターだけが含まれるようにします。 このようにすると、生成された任意のメソッドを手動で記述したコードでオーバーライドできます。
それ以外に生成される言語 (XML など) では、<#@include#> ディレクティブを使用して、手動で記述したコンテンツと生成されたコンテンツを単純に結合します。 より複雑なケースになると、生成されたファイルに手動で記述したファイルを結合する後処理のステップを作成する必要が生じることもあります。
共通の素材をインクルード ファイルまたは実行時テンプレートに移動する。
複数のテンプレートで類似するテキスト ブロックやコード ブロックを繰り返さないように、<#@ include #> ディレクティブを使用します。 詳細については、「T4 インクルード ディレクティブ」を参照してください。別のプロジェクトで実行時テキスト テンプレートをビルドし、デザイン時テンプレートから呼び出すこともできます。 そのためには、<#@ assembly #> ディレクティブを使用して、対象のプロジェクトにアクセスします。 使用例については、テキスト テンプレートでの継承に関する Gareth Jones のブログを参照してください。
コードのラージ ブロックを別のアセンブリに移動することを検討する。
コードのラージ ブロックとクラス機能ブロックがある場合は、このコードの一部を別のプロジェクトでコンパイルしたメソッドに移動すると便利です。 テンプレート内のコードには、<#@ assembly #> ディレクティブを使用してアクセスできます。 詳細については、「T4 アセンブリ ディレクティブ」を参照してください。テンプレートが継承できる抽象クラスにメソッドを配置できます。 この抽象クラスは、Microsoft.VisualStudio.TextTemplating.TextTransformation から継承する必要があります。 詳細については、「T4 テンプレート ディレクティブ」を参照してください。
構成ファイルではなく、コードを生成する。
変化に強いアプリケーションを作成する 1 つの方法は、構成ファイルを読み書きできるようにしてプログラム コードの汎用性を高めることです。 このようにして記述されたアプリケーションは柔軟性に優れているため、ビジネス要件が変わった場合にも、アプリケーションをビルドし直すことなく再構成できます。 しかし、この方法には、対象の用途に特化したアプリケーションと比べ、パフォーマンスが低くなるという欠点があります。 また、型の扱いに関しても、最も汎用的な型を選ばなければならないために、プログラム コードが読みにくく、メンテナンスが困難になる傾向があります。一方、可変部分が生成されてからコンパイルされるアプリケーションならば、厳密な型指定が可能です。 これにより、手動でのコードの記述しやすさと信頼性が格段に向上し、ソフトウェアの中で自動的に生成された部分との統一も容易になります。
実際にプログラム コードを生成してみます。コード生成の利点を詳しく知る近道は、構成ファイルを利用する代わりに実際にプログラム コードを生成してみることです。
Generated Code フォルダーを使用する。
直接編集するファイルとは明確に区別できるよう、テンプレートおよび生成されたファイルは、"Generated Code" という名前のプロジェクト フォルダーに置きます。 生成されたクラスをオーバーライド (または生成されたクラスに追加) するためのカスタム コードを作成する場合は、"Custom Code" という名前のフォルダーにそれらのクラスを格納します。 標準的なプロジェクトであれば、次のような構造になります。MyProject Custom Code Class1.cs Class2.cs Generated Code Class1.tt Class1.cs Class2.tt Class2.cs AnotherClass.cs
実行時 (前処理された) T4 テンプレートに関するガイドライン
共通の素材を継承されたテンプレートに移動する。
継承を使用して、T4 テキスト テンプレート間でメソッドやテキスト ブロックを共有できます。 詳細については、「T4 テンプレート ディレクティブ」を参照してください。実行時テンプレートが含まれたインクルード ファイルを使用することもできます。
コードの本体が大きい場合は、本体を部分クラスに移動する。
各実行時テンプレートでは、そのテンプレートと同じ名前の部分クラス定義が生成されます。 同じクラスの別の部分定義を含むコード ファイルを作成できます。 この方法でメソッド、フィールド、およびコンストラクターをクラスに追加できます。 これらのメンバーは、テンプレートのコード ブロックから呼び出すことができます。この方法では IntelliSense を利用できるため、コードの記述が簡単になるという利点があります。 また、表示形式と基になるロジックを適切に分離することもできます。
MyReportText.tt の場合:
The total is: <#= ComputeTotal() #>
MyReportText-Methods.cs の場合:
private string ComputeTotal() { ... }
カスタム コードで拡張ポイントを提供できるようにする。
<#+ class feature blocks #> で仮想メソッドを生成することを検討します。 これにより、変更を加えずに 1 つのテンプレートをさまざまなコンテキストで使用できるようになります。 テンプレートを変更するのではなく、最小限の追加ロジックを提供する派生クラスを作成できます。 派生クラスは、通常のコードでも、実行時テンプレートでもかまいません。MyStandardRunTimeTemplate.tt の場合:
This page is copyright <#= CompanyName() #>. <#+ protected virtual string CompanyName() { return ""; } #>
アプリケーション コードの場合:
class FabrikamTemplate : MyStandardRunTimeTemplate { protected override string CompanyName() { return "Fabrikam"; } } ... string PageToDisplay = new FabrikamTemplate().TextTransform();
すべての T4 テンプレートに関するガイドライン
データ収集をテキスト生成から分離する。
計算とテキスト ブロックを混在させないようにします。 各テキスト テンプレートでは、最初の <# code block #> を使用して変数を設定し、複雑な計算を実行します。 最初のテキスト ブロックからテンプレートの末尾または最初の <#+ class feature block #> まで、長い式は使用しないようにします。また、テキスト ブロックが含まれている場合を除き、ループと条件を使用しないようにします。 このようにすると、テンプレートが読みやすくなり保守が容易になります。インクルード ファイルに .tt を使用しない。
インクルード ファイルには、.ttinclude などの別のファイル名拡張子を使用します。 実行時またはデザイン時テキスト テンプレートとして処理するファイルにのみ、.tt を使用します。 Visual Studio では、.tt ファイルが認識され、処理のためにプロパティが自動的に設定される場合があります。固定のプロトタイプからテンプレートへとしあげていく。
生成するコードまたはテキストの例を記述し、正しいことを確認します。 次に、拡張子を .tt に変更し、モデルを読み取ってコンテンツを変更するコードを徐々に挿入していきます。型指定されたモデルを使用することを検討する。
モデル用に XML またはデータベース スキーマを作成することもできますが、ドメイン固有言語 (DSL) を作成すると役立ちます。 DSL には、スキーマの各ノードを表すクラスと属性を表すプロパティが生成されるという利点があります。 つまり、ビジネス モデルの観点でプログラミングできます。 次に例を示します。Team Members: <# foreach (Person p in team.Members) { #> <#= p.Name #> <# } #>
モデルの図を使用することを検討する。
多くのモデルは単にテキスト テーブルとして表現および管理するのが最も効果的です。これは、特にモデルのサイズが非常に大きい場合に当てはまります。ただし、ビジネス要件の種類によっては、関係とワーク フローの複雑なセットを明確にすることが重要となるため、図が最適なメディアと言えます。 図の利点は、ユーザーや他の関係者に説明しやすくなることです。 ビジネス要件のレベルでモデルからコードを生成することにより、要件が変わったときのコードの柔軟性を高めることができます。
多くの場合、UML クラス図と UML アクティビティ図をこれらの目的に適合させることができます。 独自の種類の図をドメイン固有言語 (DSL) として設計することもできます。 コードは、UML と DSL のどちらからでも生成できます。 詳細については、「アプリケーションのモデル化」および「アプリケーションのモデル化」を参照してください。
参照
概念
その他の技術情報
前処理された T4 テキスト テンプレートを使用した実行時テキスト生成
履歴の変更
日付 |
履歴 |
理由 |
---|---|---|
2011 年 3 月 |
フィードバックが取り入れられました。 |
カスタマー フィードバック |
2010 年 10 月 |
既存の素材に基づく新しいトピックが織り込まれました。 |
情報の拡充 |