Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Pembuatan kode memungkinkan Anda untuk menghasilkan kode program yang sangat ditik, namun dapat dengan mudah diubah ketika model sumber berubah. Berbeda dengan teknik alternatif menulis program yang sepenuhnya umum yang menerima file konfigurasi, yang lebih fleksibel, tetapi menghasilkan kode yang tidak begitu mudah dibaca dan diubah, atau memiliki performa yang baik. Panduan ini menunjukkan manfaat ini.
Kode yang di ketik untuk membaca XML
Namespace System.Xml menyediakan alat komprehensif untuk memuat dokumen XML lalu menavigasinya secara bebas dalam memori. Sayangnya, semua simpul memiliki jenis yang sama, XmlNode. Oleh karena itu sangat mudah untuk membuat kesalahan pemrograman seperti mengharapkan jenis node anak yang salah, atau atribut yang salah.
Dalam contoh proyek ini, templat membaca sampel file XML, dan menghasilkan kelas yang sesuai dengan setiap jenis simpul. Dalam kode tulisan tangan, Anda dapat menggunakan kelas-kelas ini untuk menavigasi file XML. Anda juga dapat menjalankan aplikasi Anda pada file lain yang menggunakan jenis node yang sama. Tujuan dari sampel file XML adalah untuk memberikan contoh semua jenis node yang Anda inginkan untuk ditangani oleh aplikasi Anda.
Catatan
Aplikasi xsd.exe, yang disertakan dengan Visual Studio, dapat menghasilkan kelas yang ditik dengan kuat dari file XML. Templat yang diperlihatkan di sini disediakan sebagai contoh.
Berikut adalah file sampelnya:
<?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>
Dalam proyek yang dibangun panduan ini, Anda dapat menulis kode seperti berikut ini, dan IntelliSense meminta Anda dengan atribut dan nama anak yang benar saat Anda mengetik:
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);
}
}
Kontraskan ini dengan kode yang tidak ditik yang mungkin Anda tulis tanpa templat:
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);
}
}
Dalam versi yang diketik dengan kuat, perubahan pada skema XML menghasilkan perubahan pada kelas. Pengkompilasi menyoroti bagian kode aplikasi yang harus diubah. Dalam versi yang tidak dijenis yang menggunakan kode XML generik, tidak ada dukungan seperti itu.
Dalam proyek ini, satu file templat digunakan untuk menghasilkan kelas yang memungkinkan versi yang ditik.
Menyiapkan Proyek
Membuat atau membuka proyek C#
Anda dapat menerapkan teknik ini ke proyek kode apa pun. Panduan ini menggunakan proyek C#, dan untuk tujuan pengujian, kami menggunakan aplikasi konsol.
Pada menu File, klik Baru, lalu klik Proyek.
Klik simpul Visual C#, lalu di panel Templat, klik Aplikasi Konsol.
Menambahkan file XML prototipe ke proyek
Tujuan dari file ini adalah untuk menyediakan sampel jenis simpul XML yang Anda inginkan untuk dapat dibaca oleh aplikasi Anda. Ini bisa menjadi file yang akan digunakan untuk menguji aplikasi Anda. Templat akan menghasilkan kelas C# untuk setiap jenis node dalam file ini.
File harus menjadi bagian dari proyek sehingga templat dapat membacanya, tetapi tidak akan dibangun ke dalam aplikasi yang dikompilasi.
Di Penjelajah Solusi, klik kanan proyek, klik Tambahkan lalu Klik Item Baru.
Dalam kotak dialog Tambahkan Item Baru, pilih File XML dari panel Templat.
Tambahkan konten sampel Anda ke file.
Untuk panduan ini, beri nama file
exampleXml.xml
. Atur konten file menjadi XML yang diperlihatkan di bagian sebelumnya.
Menambahkan file kode pengujian
Tambahkan file C# ke proyek Anda dan tulis di dalamnya sampel kode yang ingin Anda tulis. Misalnya:
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);
} } } } }
Pada tahap ini, kode ini akan gagal dikompilasi. Saat Anda menulis templat, Anda akan menghasilkan kelas yang memungkinkannya berhasil.
Pengujian yang lebih komprehensif dapat memeriksa output fungsi pengujian ini terhadap konten yang diketahui dari contoh file XML. Tetapi dalam panduan ini, kita akan puas ketika metode pengujian dikompilasi.
Menambahkan file templat teks
Tambahkan file templat teks, dan atur ekstensi output ke .cs.
Di Penjelajah Solusi, klik kanan proyek, klik Tambahkan, lalu klik Item Baru.
Dalam kotak dialog Tambahkan Item Baru pilih Templat Teks dari panel Templat.
Catatan
Pastikan Anda menambahkan Templat Teks, dan bukan Templat Teks yang Telah Diproses sebelumnya.
Dalam file, dalam direktif templat, ubah atribut
hostspecific
menjaditrue
.Perubahan ini akan memungkinkan kode templat untuk mendapatkan akses ke layanan Visual Studio.
Dalam arahan output, ubah atribut ekstensi menjadi ".cs", sehingga templat menghasilkan file C#. Dalam proyek Visual Basic, Anda akan mengubahnya menjadi ".vb".
Simpan file. Pada tahap ini, file templat teks harus berisi baris-baris ini:
<#@ template debug="false" hostspecific="true" language="C#" #> <#@ output extension=".cs" #>
Perhatikan bahwa file .cs muncul di Penjelajah Solusi sebagai anak perusahaan file templat. Anda dapat melihatnya dengan mengklik [+] di samping nama file templat. File ini dihasilkan dari file templat setiap kali Anda menyimpan atau memindahkan fokus dari file templat. File yang dihasilkan akan dikompilasi sebagai bagian dari proyek Anda.
Untuk kenyamanan saat Anda mengembangkan file templat, atur jendela file templat dan file yang dihasilkan sehingga Anda dapat melihatnya di samping satu sama lain. Ini memungkinkan Anda melihat langsung output templat Anda. Anda juga akan melihat bahwa ketika templat Anda menghasilkan kode C# yang tidak valid, kesalahan akan muncul di jendela pesan kesalahan.
Setiap pengeditan yang Anda lakukan langsung dalam file yang dihasilkan akan hilang setiap kali Anda menyimpan file templat. Oleh karena itu, Anda harus menghindari pengeditan file yang dihasilkan, atau mengeditnya hanya untuk eksperimen singkat. Terkadang berguna untuk mencoba fragmen kode singkat dalam file yang dihasilkan, di mana IntelliSense sedang beroperasi, lalu menyalinnya ke file templat.
Mengembangkan Templat Teks
Mengikuti saran terbaik tentang pengembangan tangkas, kami akan mengembangkan templat dalam langkah-langkah kecil, menghapus beberapa kesalahan pada setiap tahapan, sampai kode pengujian dikompilasi dan berjalan dengan benar.
Prototipe kode yang akan dihasilkan
Kode pengujian memerlukan kelas untuk setiap simpul dalam file. Oleh karena itu, beberapa kesalahan kompilasi akan hilang jika Anda menambahkan baris ini ke templat, lalu menyimpannya:
class Catalog {}
class Artist {}
class Song {}
Ini membantu Anda melihat apa yang diperlukan, tetapi deklarasi harus dihasilkan dari jenis node dalam file XML sampel. Hapus baris eksperimental ini dari templat.
Membuat kode aplikasi dari file XML model
Untuk membaca file XML dan menghasilkan deklarasi kelas, ganti konten templat dengan kode templat berikut:
<#@ 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 #> {}
<#
}
#>
Ganti jalur file dengan jalur yang benar untuk proyek Anda.
Perhatikan pembatas blok kode <#...#>
. Pemisah ini memetakan fragmen kode program yang menghasilkan teks. Pembatas blok ekspresi <#=...#>
mengurungkan ekspresi yang dapat dievaluasi ke string.
Saat Anda menulis templat yang menghasilkan kode sumber untuk aplikasi Anda, Anda berurusan dengan dua teks program terpisah. Program di dalam pembatas blok kode berjalan setiap kali Anda menyimpan templat atau memindahkan fokus ke jendela lain. Teks yang dihasilkannya, yang muncul di luar pemisah, disalin ke file yang dihasilkan dan menjadi bagian dari kode aplikasi Anda.
Petunjuk <#@assembly#>
berperilaku seperti referensi, membuat rakitan tersedia untuk kode template. Daftar rakitan yang dilihat oleh templat terpisah dari daftar Referensi dalam proyek aplikasi.
Petunjuk <#@import#>
bertindak seperti pernyataan using
, memungkinkan Anda menggunakan nama pendek kelas di namespace yang diimpor.
Sayangnya, meskipun templat ini menghasilkan kode, templat ini menghasilkan deklarasi kelas untuk setiap simpul dalam contoh file XML, sehingga jika ada beberapa instans simpul <song>
, beberapa deklarasi lagu kelas akan muncul.
Baca file model, lalu hasilkan kode
Banyak templat teks mengikuti pola di mana bagian pertama templat membaca file sumber, dan bagian kedua menghasilkan templat. Kita perlu membaca semua file contoh untuk meringkas jenis node yang dikandungnya, lalu menghasilkan deklarasi kelas. Hal lain <#@import#>
diperlukan agar kita dapat menggunakan 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 #> {}
<#
}
#>
Menambahkan metode tambahan
Blok kontrol fitur kelas adalah blok tempat Anda dapat menentukan metode tambahan. Blok dibatasi oleh <#+...#>
dan harus muncul sebagai blok terakhir dalam file.
Jika Anda lebih suka nama kelas dimulai dengan huruf besar, Anda dapat mengganti bagian terakhir templat dengan kode templat berikut:
// 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); }
#>
Pada tahap ini, file .cs yang dihasilkan berisi deklarasi berikut:
public partial class Catalog {}
public partial class Artist {}
public partial class Song {}
Detail selengkapnya seperti properti untuk simpul anak, atribut, dan teks dalam dapat ditambahkan menggunakan pendekatan yang sama.
Mengakses API Visual Studio
Mengatur atribut hostspecific
dari petunjuk <#@template#>
memungkinkan templat memperoleh akses ke Visual Studio API. Templat dapat menggunakan ini untuk mendapatkan lokasi file proyek, untuk menghindari penggunaan jalur file absolut dalam kode templat.
<#@ 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"));
Lengkapi Templat Teks
Konten templat berikut menghasilkan kode yang memungkinkan kode pengujian dikompilasi dan dijalankan.
<#@ 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);
}
#>
Jalankan program pengujian
Di utama aplikasi konsol, baris berikut akan menjalankan metode pengujian. Tekan F5 untuk menjalankan program dalam mode debug:
using System;
namespace MyProject
{
class Program
{
static void Main(string[] args)
{
new CodeGeneratorTest().TestMethod();
// Allow user to see the output:
Console.ReadLine();
}
}
}
Menulis dan memperbarui aplikasi
Aplikasi sekarang dapat ditulis dengan gaya yang ditik dengan kuat, menggunakan kelas yang dihasilkan alih-alih menggunakan kode XML generik.
Ketika skema XML berubah, kelas baru dapat dengan mudah dihasilkan. Pengkompilasi akan memberi tahu pengembang di mana kode aplikasi harus diperbarui.
Untuk meregenerasi kelas saat contoh file XML diubah, klik Ubah Semua Templat di toolbar Penjelajah Solusi.
Kesimpulan
Panduan ini menunjukkan beberapa teknik dan manfaat pembuatan kode:
Pembuatan kode adalah pembuatan bagian dari kode sumber aplikasi Anda dari model. Model ini berisi informasi dalam formulir yang cocok untuk domain aplikasi, dan dapat berubah selama masa pakai aplikasi.
Pengetikan yang kuat adalah salah satu manfaat pembuatan kode. Meskipun model mewakili informasi dalam bentuk yang lebih cocok untuk pengguna, kode yang dihasilkan memungkinkan bagian lain dari aplikasi untuk menangani informasi menggunakan sekumpulan jenis.
IntelliSense dan pengkompilasi membantu Anda membuat kode yang mematuhi skema model, baik saat Anda menulis kode baru maupun saat skema diperbarui.
Penambahan satu file templat yang tidak rumit ke proyek dapat memberikan manfaat ini.
Templat teks dapat dikembangkan dan diuji dengan cepat dan bertahap.
Dalam panduan ini, kode program sebenarnya dihasilkan dari instans model, contoh representatif dari file XML yang akan diproses aplikasi. Dalam pendekatan yang lebih formal, skema XML akan menjadi input ke templat, dalam bentuk file .xsd atau definisi bahasa khusus domain. Pendekatan itu akan memudahkan templat untuk menentukan karakteristik seperti kelipatan hubungan.
Memecahkan masalah Templat Teks
Jika Anda telah melihat transformasi templat atau kesalahan kompilasi di Daftar Kesalahan, atau jika file output tidak dihasilkan dengan benar, Anda dapat memecahkan masalah templat teks dengan teknik yang dijelaskan dalam Menghasilkan File dengan Utilitas TextTransform.