Udostępnij za pośrednictwem


Instruktaż: Generowanie kodu za pomocą szablonów tekstu

Generowanie kodu pozwala produkować kodu programu, która ma jednoznacznie określony i jeszcze można łatwo zmieniać podczas zmiany modelu źródłowego.Przeciwieństwem tego alternatywne techniki pisania całkowicie generyczne program, który akceptuje plik konfiguracyjny, który jest bardziej elastyczne, ale jego rezultaty w kodzie, który nie jest ani tak łatwo odczytać i zmienić, ani nie ma takich dobrej wydajności.W tym instruktażu przedstawiono tej korzyści.

Kod maszynowy do czytania XML

Obszar nazw System.Xml udostępnia wszechstronne narzędzia do ładowania dokumentu XML, a następnie przechodząc ją swobodnie w pamięci.Niestety wszystkie węzły mają tego samego typu, XmlNode.Dlatego jest bardzo łatwo popełnić błędy programowania takich jak oczekiwano niewłaściwy typ węzła podrzędnego lub nieprawidłowe atrybuty.

W tym projekcie przykład szablonu odczytuje przykładowego pliku XML i generuje klas, które odpowiadają każdego typu węzła.Kod napisany ręcznie można użyć tych klas, aby przejść do pliku XML.Można również uruchomić aplikację na innych plików, które używają tych samych typów węzłów.Celem przykładowego pliku XML jest zawierają przykłady wszystkie typy węzłów, które aplikacji w celu zajmowania się.

[!UWAGA]

Aplikacja xsd.exe, która jest dostarczana z Visual Studio, można wygenerować jednoznacznie określone typy klas z plików XML.Szablon, tu znajduje się na przykład.

Oto przykładowy plik:

<?xml version="1.0" encoding="utf-8" ?>
<catalog>
  <artist id ="Mike%20Nash" name="Mike Nash Quartet">
    <song id ="MikeNashJazzBeforeTeatime">Jazz Before Teatime</song>
    <song id ="MikeNashJazzAfterBreakfast">Jazz After Breakfast</song>
  </artist>
  <artist id ="Euan%20Garden" name="Euan Garden">
    <song id ="GardenScottishCountry">Scottish Country Garden</song>
  </artist>
</catalog>

W projekcie, w tym instruktażu konstrukcje, można napisać kod, taki jak poniższy, i IntelliSense monituje o poprawne nazwy atrybutu i dziecko podczas pisania:

Catalog catalog = new Catalog(xmlDocument);
foreach (Artist artist in catalog.Artist)
{
  Console.WriteLine(artist.name);
  foreach (Song song in artist.Song)
  {
    Console.WriteLine("   " + song.Text);
  }
}

Przeciwieństwem tego bez typu kod, który mógłby napisać bez szablonu:

XmlNode catalog = xmlDocument.SelectSingleNode("catalog");
foreach (XmlNode artist in catalog.SelectNodes("artist"))
{
    Console.WriteLine(artist.Attributes["name"].Value);
    foreach (XmlNode song in artist.SelectNodes("song"))
    {
         Console.WriteLine("   " + song.InnerText);
     }
}

W wersji jednoznacznie zmiany schematu XML spowoduje zmiany do klas.Kompilator będzie wyróżniania fragmentów kodu aplikacji, która musi zostać zmieniona.W wersji bez typu rodzajowego kodu XML używa nie istnieje żadne takie wsparcie.

W tym projekcie jednego pliku szablonu służy do generowania klas, które umożliwiają maszynowy wersji.

Definiowanie projektu

Dd820614.collapse_all(pl-pl,VS.110).gifUtwórz lub Otwórz projekt C#

Ta technika można zastosować do projektu dowolnego kodu.W tym instruktażu wykorzystano projekt C# i do celów badania wykorzystujemy aplikację konsoli.

Aby utworzyć projekt

  1. Na pliku kliknij menu Nowy , a następnie kliknij przycisk Projekt.

  2. Kliknij przycisk Visual C# węzeł, a następnie w szablonów okienka, kliknij aplikacji konsoli.

Dd820614.collapse_all(pl-pl,VS.110).gifDodaj plik XML prototyp do projektu

Celem tego pliku jest podać próbki typy węzłów XML, które aplikacji, aby można było odczytać.Być może plik, który będzie używany do testowania aplikacji.Szablon powoduje wygenerowanie klasy C# dla każdego typu węzła, w tym pliku.

Plik powinien być częścią projektu, tak, aby szablon może go odczytać, ale nie będzie ona wbudowana do skompilowanej aplikacji.

Aby dodać plik XML

  1. W Solution Explorer, kliknij prawym przyciskiem myszy projekt, kliknij przycisk Dodaj , a następnie kliknij przycisk Nowego elementu.

  2. W Dodaj nowy element okno dialogowe Wybierz Pliku XML z szablonów okienka.

  3. Dodać zawartość próbki do pliku.

  4. Dla tego instruktażu, nazwę pliku exampleXml.xml.Ustaw zawartość pliku XML, przedstawione w poprzedniej sekcji.

..

Dd820614.collapse_all(pl-pl,VS.110).gifDodawanie pliku kodu testu

Dodawanie pliku C# do projektu i zapisać w nim próbki kodu, który chcesz mieć możliwość zapisywania.Na przykład:

using System;
namespace MyProject
{
  class CodeGeneratorTest
  {
    public void TestMethod()
    {
      Catalog catalog = new Catalog(@"..\..\exampleXml.xml");
      foreach (Artist artist in catalog.Artist)
      {
        Console.WriteLine(artist.name);
        foreach (Song song in artist.Song)
        {
          Console.WriteLine("   " + song.Text);
} } } } }

Na tym etapie tego kodu nie będzie można kompilować.Podczas pisania szablonu spowoduje wygenerowanie klasy umożliwiające powiodła się.

Bardziej kompleksowe badanie można sprawdzić dane wyjściowe tej funkcji test przeciwko znanych zawartości przykładowy plik XML.Jednak, w tym instruktażu zostaną spełnione podczas kompiluje metody badania.

Dd820614.collapse_all(pl-pl,VS.110).gifDodaj plik szablonu tekstu

Dodaj plik szablonu tekst, a następnie ustaw rozszerzenie wyjście ".cs".

Aby dodać plik szablonu tekstu do projektu

  1. W Solution Explorer, kliknij prawym przyciskiem myszy projekt, kliknij przycisk Dodaj, a następnie kliknij przycisk Nowego elementu.

  2. W Dodaj nowy element zaznacz pole dialogowe Tekst szablonu z szablonów okienka.

    [!UWAGA]

    Upewnij się, że dodawanie szablonu tekstu i nie Preprocessed tekst szablonu.

  3. W pliku, w dyrektywie szablonu zmienić hostspecific atrybutu do true.

    Zmiana ta umożliwi kod szablonu w celu uzyskania dostępu do Visual Studio usług.

  4. W dyrektywy wyjściowej zmień atrybut rozszerzenia na ".cs", tak, aby w szablonie zostanie wygenerowany plik języka C#.W projekcie programu Visual Basic będzie zmienić na ".vb".

  5. Zapisz plik.Na tym etapie plik szablonu tekst powinien zawierać te wiersze:

    <#@ template debug="false" hostspecific="true" language="C#" #>
    <#@ output extension=".cs" #>
    

.

Zauważ, że pliku cs pojawia się w oknie Solution Explorer zależna od pliku szablonu.Można go wyświetlić klikając przycisk [+] obok nazwy pliku szablonu.Ten plik jest generowany na podstawie pliku szablonu zawsze zapisać lub przenieść fokus poza pliku szablonu.Wygenerowany plik będą kompilowane jako część projektu.

Dla wygody podczas rozwijania pliku szablonu, rozmieszczanie systemu windows plik szablonu i wygenerowany plik tak, aby widoczne obok siebie.Dzięki temu można natychmiast zobaczyć dane wyjściowe z szablonu.Będzie również zauważyć, że gdy szablon generuje nieprawidłowy kod C#, błędy zostaną wyświetlone w oknie komunikatu błąd.

Wszelkich zmian, które można wykonywać bezpośrednio w wygenerowanym pliku zostaną utracone przy każdym zapisywaniu pliku szablonu.Należy zatem uniknięcia edycji wygenerowany plik, lub go edytować tylko w przypadku krótkich eksperymentów.Czasami warto spróbować krótki fragment kodu w pliku generowanych, gdy technologia IntelliSense jest w operacji, a następnie skopiuj go do pliku szablonu.

Rozwijanie szablonu tekstu

W następstwie Najlepsza Rada na rozwój agile możemy opracuje szablonu w małych kroków, wyczyszczenie niektórych błędów na każdym przyrostu aż kodu testu kompiluje i działa poprawnie.

Dd820614.collapse_all(pl-pl,VS.110).gifPrototyp będzie generowany kod

Kod testu wymaga klasy dla każdego węzła w pliku.W związku z tym niektóre błędy kompilacji będzie go pozbyć, jeżeli Dołącz te wiersze do szablonu, a następnie zapisz go:

  class Catalog {} 
  class Artist {}
  class Song {}

Dzięki temu można zobaczyć, co jest wymagane, ale deklaracje powinny być generowane z typy węzłów w przykładowego pliku XML.Z szablonu, należy usunąć te wiersze doświadczalnych.

Dd820614.collapse_all(pl-pl,VS.110).gifGenerowanie kodu aplikacji z pliku XML modelu

Aby odczytać plik XML i wygenerować deklaracje klas, zastąpić szablon zawartości z następujący kod szablonu:

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Xml"#>
<#@ import namespace="System.Xml" #>
<#
 XmlDocument doc = new XmlDocument();
 // Replace this file path with yours:
 doc.Load(@"C:\MySolution\MyProject\exampleXml.xml");
 foreach (XmlNode node in doc.SelectNodes("//*"))
 {
#>
  public partial class <#= node.Name #> {}
<#
 }
#>

Ścieżka do pliku należy zastąpić poprawną ścieżkę dla projektu.

Zawiadomienie ograniczniki blok kodu <#...#>.Tymi ogranicznikami nawiasu fragment kodu programu, która generuje tekst.Ograniczniki bloku wyrażenie <#=...#> nawiasu wyrażenie, które może zostać przyrównane do ciągu.

Pisząc szablonu, który generuje kod źródłowy aplikacji, mamy do czynienia z dwoma tekstami oddzielnego programu.Program wewnątrz ograniczników blok kodu jest uruchamiany zawsze zapisać szablon, lub przeniesienie fokusu do innego okna.Tekst, który generuje i pojawia się poza ograniczniki, jest kopiowany do pliku wygenerowanego i staje się częścią kodu aplikacji.

<#@assembly#> Dyrektywy zachowuje się jak odniesienie, udostępnienie zestawu kodu szablonu.Lista zestawów widziane przez szablon jest oddzielone od listy odwołania w projekcie aplikacji.

<#@import#> Dyrektywy działa podobnie jak using instrukcji, pozwala na użycie krótkich nazw klas w zaimportowanego obszaru nazw.

Niestety, chociaż ten szablon generuje kod, produkuje deklaracji klasy dla każdego węzła w przykładowy plik XML tak, że jeśli istnieje kilka wystąpień <song> węzła, pojawi się kilka deklaracji song klasy.

Dd820614.collapse_all(pl-pl,VS.110).gifPrzeczytaj plik modelu, a następnie wygenerować kod

Wiele szablonów tekstu wykonaj wzorca, w którym pierwsza część szablonu odczytuje plik źródłowy, a druga część generuje szablonu.Musimy się odczytu wszystkich przykładowy plik, aby podsumować typy węzłów, które on zawiera, a następnie wygeneruj deklaracje klas.Innym <#@import#> jest potrzebne, dzięki czemu możemy użyćDictionary<>:

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Xml"#>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Collections.Generic" #>
<#
 // Read the model file
 XmlDocument doc = new XmlDocument();
 doc.Load(@"C:\MySolution\MyProject\exampleXml.xml");
 Dictionary <string, string> nodeTypes = 
        new Dictionary<string, string>();
 foreach (XmlNode node in doc.SelectNodes("//*"))
 {
   nodeTypes[node.Name] = "";
 }
 // Generate the code
 foreach (string nodeName in nodeTypes.Keys)
 {
#>
  public partial class <#= nodeName #> {}
<#
 }
#>

Dd820614.collapse_all(pl-pl,VS.110).gifDodaj metodę pomocniczy

Blok sterowania funkcją klasy jest blok w którym można zdefiniować metody pomocnicze.Blok jest ograniczony przez <#+...#> i muszą pojawiać się jako ostatni bloku w pliku.

Jeśli wolisz nazwy klasy rozpoczynał się od wielkiej litery, ostatnia część szablonu można zastąpić następujący kod szablonu:

// Generate the code
 foreach (string nodeName in nodeTypes.Keys)
 {
#>
  public partial class <#= UpperInitial(nodeName) #> {}
<#
 }
#>
<#+
 private string UpperInitial(string name)
 { return name[0].ToString().ToUpperInvariant() + name.Substring(1); }
#>

Na tym etapie plik wygenerowany CS zawiera następujące deklaracje:

  public partial class Catalog {}
  public partial class Artist {}
  public partial class Song {}

Więcej szczegółów, takich jak właściwości dla węzłów podrzędnych, atrybuty i wewnętrzny tekst można dodać przy użyciu tego samego podejścia.

Dd820614.collapse_all(pl-pl,VS.110).gifUzyskiwanie dostępu do interfejsu API programu Visual Studio

Ustawienie hostspecific atrybutu <#@template#> dyrektywa zezwala szablonu do uzyskania dostępu do Visual Studio interfejsu API.Szablon, można użyć to uzyskanie lokalizacji plików projektu w celu uniknięcia, podając ścieżkę bezwzględną pliku w kodzie szablonu.

<#@ template debug="false" hostspecific="true" language="C#" #>
...
<#@ assembly name="EnvDTE" #>
...
EnvDTE.DTE dte = (EnvDTE.DTE) ((IServiceProvider) this.Host)
                       .GetService(typeof(EnvDTE.DTE));
// Open the prototype document.
XmlDocument doc = new XmlDocument();
doc.Load(System.IO.Path.Combine(dte.ActiveDocument.Path, "exampleXml.xml"));

Korzystanie z szablonu tekstu

Następujące zawartość szablonu generuje kod, który umożliwia kod test skompilować i uruchomić.

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Xml" #>
<#@ assembly name="EnvDTE" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Collections.Generic" #>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
namespace MyProject
{
<#
 // Map node name --> child name --> child node type
 Dictionary<string, Dictionary<string, XmlNodeType>> nodeTypes = new Dictionary<string, Dictionary<string, XmlNodeType>>();

 // The Visual Studio host, to get the local file path.
 EnvDTE.DTE dte = (EnvDTE.DTE) ((IServiceProvider) this.Host)
                       .GetService(typeof(EnvDTE.DTE));
 // Open the prototype document.
 XmlDocument doc = new XmlDocument();
 doc.Load(System.IO.Path.Combine(dte.ActiveDocument.Path, "exampleXml.xml"));
 // Inspect all the nodes in the document.
 // The example might contain many nodes of the same type, 
 // so make a dictionary of node types and their children.
 foreach (XmlNode node in doc.SelectNodes("//*"))
 {
   Dictionary<string, XmlNodeType> subs = null;
   if (!nodeTypes.TryGetValue(node.Name, out subs))
   {
     subs = new Dictionary<string, XmlNodeType>();
     nodeTypes.Add(node.Name, subs);
   }
   foreach (XmlNode child in node.ChildNodes)
   {
     subs[child.Name] = child.NodeType;
   } 
   foreach (XmlNode child in node.Attributes)
   {
     subs[child.Name] = child.NodeType;
   }
 }
 // Generate a class for each node type.
 foreach (string className in nodeTypes.Keys)
 {
    // Capitalize the first character of the name.
#>
    partial class <#= UpperInitial(className) #>
    {
      private XmlNode thisNode;
      public <#= UpperInitial(className) #>(XmlNode node) 
      { thisNode = node; }

<#
    // Generate a property for each child.
    foreach (string childName in nodeTypes[className].Keys)
    {
      // Allow for different types of child.
      switch (nodeTypes[className][childName])
      {
         // Child nodes:
         case XmlNodeType.Element:
#>
      public IEnumerable<<#=UpperInitial(childName)#>><#=UpperInitial(childName) #>
      { 
        get 
        { 
           foreach (XmlNode node in
                thisNode.SelectNodes("<#=childName#>")) 
             yield return new <#=UpperInitial(childName)#>(node); 
      } }
<#
         break;
         // Child attributes:
         case XmlNodeType.Attribute:
#>
      public string <#=childName #>
      { get { return thisNode.Attributes["<#=childName#>"].Value; } }
<#
         break;
         // Plain text:
         case XmlNodeType.Text:
#>
      public string Text  { get { return thisNode.InnerText; } }
<#
         break;
       } // switch
     } // foreach class child
  // End of the generated class:
#>
   } 
<#
 } // foreach class

   // Add a constructor for the root class 
   // that accepts an XML filename.
   string rootClassName = doc.SelectSingleNode("*").Name;
#>
   partial class <#= UpperInitial(rootClassName) #>
   {
      public <#= UpperInitial(rootClassName) #>(string fileName) 
      {
        XmlDocument doc = new XmlDocument();
        doc.Load(fileName);
        thisNode = doc.SelectSingleNode("<#=rootClassName#>");
      }
   }
}
<#+
   private string UpperInitial(string name)
   {
      return name[0].ToString().ToUpperInvariant() + name.Substring(1);
   }
#>

Dd820614.collapse_all(pl-pl,VS.110).gifUruchomiony program badania

W głównym aplikacji konsoli następujące wiersze, będzie wykonywał metody badania.Naciśnij klawisz F5, aby uruchomić program w trybie debugowania:

using System;
namespace MyProject
{ class Program
  { static void Main(string[] args)
    { new CodeGeneratorTest().TestMethod();
      // Allow user to see the output:
      Console.ReadLine();
} } }

Dd820614.collapse_all(pl-pl,VS.110).gifPisanie i aktualizacja aplikacji

Aplikacja napisana teraz jednoznacznie określony styl przy użyciu klas generowanych zamiast używania uniwersalnego kodu XML.

Po zmianie schematu XML, można łatwo generowane nowych klas.Kompilator informuje autora, gdzie należy zaktualizować kod aplikacji.

Aby ponownie wygenerować klas, gdy zostanie zmieniona przykładowy plik XML, kliknij przycisk Transform wszystkie szablony na pasku narzędziowym panelu Solution Explorer.

Zawarcia

W tym instruktażu przedstawiono kilka technik i korzyści generowanie kodu:

  • Generowanie kodu jest stworzenie części kodu źródłowego aplikacji z model.Model zawiera informacje w formularzu, nadaje się do domeny aplikacji i może ulec zmianie w okresie istnienia aplikacji.

  • Wpisywanie silne jest jedną z korzyści płynących z generowania kodu.Chociaż model reprezentuje informacje w formie bardziej odpowiednie dla użytkownika, wygenerowany kod umożliwia innych części aplikacji do zajmowania się informacje przy użyciu zestaw typów.

  • Technologia IntelliSense i kompilator ułatwiają tworzenie kodu zgodnego ze schematem modelu, zarówno podczas pisania kodu nowych, jak i podczas aktualizacji schematu.

  • Dodanie jednego nieskomplikowaną pliku szablonu projektu można te świadczenia.

  • Szablon tekstu można rozwinięte i przetestowane, szybko i stopniowo.

W tym instruktażu kod programu faktycznie generowany jest wystąpienie modelu, reprezentatywna próbka pliki XML, które aplikacja będzie przetwarzać.W bardziej formalnego podejścia schematu XML byłoby dane wejściowe do szablonu, w postaci pliku XSD lub definicja języka specyficzne dla domeny.Takie podejście byłoby ułatwiają szablonu do określenia cech, takich jak liczebności relacji.

Rozwiązywanie problemów z szablonu tekstu

Jeśli widzieliśmy szablonu transformacji lub kompilacji błędów w Listy błędów, lub jeśli plik wyjściowy nie został wygenerowany poprawnie, można rozwiązać szablonu tekst z technik opisanych w Generowanie plików przy użyciu narzędzia TextTransform.

Zobacz też

Koncepcje

Generowanie kodu czasu projektowania przy użyciu szablonów tekst T4

Zapisywanie szablonu tekst T4