Generování kódu pomoci T4 šablon, část 1

Tomáš Holan       3. 6. 2013       Visual Studio       7181 zobrazení

T4 (Text Template Transformation Toolkit) je obecný šablonovací systém pro generování textového výstupu, který je navíc přímo integrovaný do Visual Studia (jestli se nemýlím tak již od VS 2008). Přestože lze v případě potřeby transformaci T4 šablony (T4 template) spouštět i kódem v runtime, primárně je T4 využívané pro generovaní výstupu v design-time tj. právě z Visual Studia.

T4 slouží obecně pro generování jakéhokoliv textového výstupu tj. zdrojového kódu v C#, Visual Basic .NET, XAML, T-SQL, nebo XML, .config nebo jiných textových souborů. My se v této sérii ale zaměříme pouze na využití T4 šablon pro generování zdrojového kódu (konkrétně v C# tj. .cs souborů).

Soubor T4 šablony má příponu .tt a jeho vlastnost Custom Tool je nastavena na hodnotu “TextTemplatingFileGenerator”. Obsahem souboru T4 šablony jsou direktivy a střídání bloků kódu s bloky textu, které se rovnou opisují na výstup (podobně jako například u ASP). Ve Visual Studiu vytvoříme novou T4 šablonu tak, že zvolíme Add, New Item a vybereme Text Template (*).

Výchozí způsob je takový, že jedna T4 šablona generuje jeden výstupní soubor, ale později v této sérii ukáži i jak lze pomoci jedné šablony generovat výstupních souborů několik. Generovaný výstupní soubor šablony je ve VS projektu zobrazován pod souborem se šablonou.

Protože je transformace šablony realizovaná standardně jako Custom Tool, je výstup šablony přegenerován v případě že:

  • Automaticky pokud změníme a uložíme soubor T4 šablony.
  • Na šabloně explicitně zvolíme volbu Run Custom Tool.

Z toho plyne, že pokud šablona používá jako vstup externí soubory nebo zdroje, musíme po jejich změně spustit transformaci ručně. Případně lze tuto nutnost obejít nastavením spouštění transformace všech šablon před každým buildem.

Pro podporu při editaci T4 šablony ve Visual Studiu je potřeba doinstalovat doplněk. Přestože není jediný, já aktuálně používám tangible T4 Editor 2.1 plus modeling tools for VS 2012. Tento doplněk existuje v placené i neplacené omezené verzi.

Syntaxe T4 šablony je popsána zde nebo zde. Ve stručnosti shrnu to nejdůležitější:

<#@ direktiva #>
např.:
Direktiva šablony
<#@ template language="C#" #> Definuje, že šablona bude napsána v jazyce C#.
<#@ output extension=".generated.cs" #> Definuje příponu generovaného výstupního souboru (tj. pokud se šablona bude například jmenovat DataObjects.tt, bude se výstupní soubor v tomto případě jmenovat DataObjects.generated.cs).
<#@ assembly name="System.Core" #> Reference na assembly.
<#@ import namespace="System.Linq" #> Import namespace (obdoba using v C#).
<#@ include file="base.tt" #> Vložení textu nebo kódu z jiného souboru T4 šablony na dané místo.
Text Blok textu, který je opsán na výstup. Blok textu lze v šabloně použít všude tam, kde je platné volání metody Write.
<# ... #> Blok kódu
<#=#> Výraz. Výsledek výrazu je opsán na výstup.
<#+ ... #> Class feature blok umožňující definovat vlastnosti, pomocné metody, nested třídy apod.. Definované členy lze využívat v hlavním kódu šablony.

Příklady použití jednotlivých bloků naleznete na výše uvedených odkazech nebo dále v příkladech v této sérii.

Bližší vysvětlení si zasluhuje podmínka platného volání metody Write u bloku textu, proto se u ní teď trochu zastavíme.

Při generování výstupu T4 šablony je nejprve na pozadí ze šablony vytvořena a zkompilována třída (**) odvozená ze základní třídy TextTransformation, následně je vytvořena její instance a na ní zavolána metoda TransformText. Návratová hodnota z tohoto volání je uložena do výstupního souboru.

Při vytváření transformační třídy:

  • jsou členy definované v class feature bloku umístěny jako členy této třídy
  • bloky kódu jsou umístěny do metody TransformText
  • bloky textu a výrazy jsou přeloženy jako volání metody Write s daným textem nebo výsledkem výrazu.

Zmíněné volání metody Write ale nemusí být nutně volání metody Write základní třídy TextTransformation, stačí když ve třídě, kde je blok textu umístěn, existuje metoda s danou signaturou:

void Write(string textToAppend);

která bude zápis výstupu provádět. Toho je možné využít pro vyčlenění části logiky šablony do pomocné třídy mimo hlavní kód šablony.

Jednotlivé typy bloků T4 šablony i výše zmíněný postup demonstruje následující příklad:

<#@ template language="C#" debug="true" #>
<#@ output extension=".generated.cs" #>
<#
    WriteHeader();
#>

namespace <#= System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("NamespaceHint") #> 
{
<#
    string[] classes = new[] { "Alpha", "Bravo", "Charlie" };

    this.PushIndent("\t");

    bool isFirstClass = true;
    foreach (string className in classes)
    {
        if (!isFirstClass)
        {
            WriteLine(null);
        }

        //Generování a zápis výstupu pro třídu
        var classFormatter = new ClassFormatter(this, className);
        classFormatter.WriteClassOutput();

        isFirstClass = false;
    }

    this.PopIndent();
#>
}<#+
    private void WriteHeader()
    { 
#>
// ------------------------------------------------------------------------------
// This code was generated by template.
//
// Changes to this file will be lost if the code is regenerated.
// -----------------------------------------------------------------------------
using System;
<#+
    }

    private sealed class ClassFormatter
    {
        #region member varible and default property initialization
        private readonly Microsoft.VisualStudio.TextTemplating.TextTransformation Owner;
        private readonly string ClassName;
        #endregion

        #region constructors and destructors
        public ClassFormatter(Microsoft.VisualStudio.TextTemplating.TextTransformation owner, string className)
        {
            this.Owner = owner;
            this.ClassName = className;
        }
        #endregion

        #region action methods
        public void WriteClassOutput()
        {
#>
public class <#= this.ClassName #>
{
    public override string ToString()
    {
        return "Hello from <#= this.ClassName #>";
    }
}
<#+
        }
        #endregion

        #region private member functions
        private void Write(string textToAppend)
        {
            this.Owner.Write(textToAppend);
        }
        #endregion
    }
#>

Výstup z této šablony bude:

// ------------------------------------------------------------------------------
// This code was generated by template.
//
// Changes to this file will be lost if the code is regenerated.
// -----------------------------------------------------------------------------
using System;

namespace T4Sample1
{
    public class Alpha
    {
        public override string ToString()
        {
            return "Hello from Alpha";
        }
    }

    public class Bravo
    {
        public override string ToString()
        {
            return "Hello from Bravo";
        }
    }

    public class Charlie
    {
        public override string ToString()
        {
            return "Hello from Charlie";
        }
    }
}

V příkladu je právě v pomocné (nested) třídě ClassFormatter definována metoda Write. Jenom díky její existenci je možné kombinovat i kód této třídy s bloky textu a s výrazy podobně jako to lze u hlavního kódu šablony nebo u instančních metod šablony (příkladem takové metody je metoda WriteHeader).

Dále bych u tohoto příkladu ještě upozornil na následující:

  • Výraz System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("NamespaceHint") vrací název namespace odpovídající projektu a podadresáři, ve kterém je soubor šablony umístěn.
  • Pomoci metod PushIndent a PopIndent lze řídit odsazení generovaného výstupu.

Příště: Rozebereme si možné vstupy a způsoby, které lze v T4 šabloně použít pro generování kódu.


(*) Některé doplňky do Visual Studia týkajících se rozšíření podpory pro editaci T4 šablon mohou tuto volbu změnit.

(**) S názvem GeneratedTextTransformation.

 

hodnocení článku

0       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback