最近在開發自動化套件,想在自己寫的程式產生器中借用T4產生Code。
典型T4應用多發生於專案編輯階段,透過存檔動作或PowerShell Script產生程式碼。簡單嘗試後,發現T4早已設想周到,在程式中用T4產生文字是小菜一碟,透過Runtime Text Template(執行階段文字範本)即可輕鬆達成, MSDN有篇詳細說明可以參考。
我做了一個簡單範例,從PTT取得文章清單後,透過T4輸出成文字檔。
首先,在專案中新增一個Runtime Text Template: (在General分類下,直接用關鍵字"template"過濾比較快)
與傳統T4範本不同,每個Runtime T4範本都附加了同名的cs檔案,定義同名的類別物件(在本例中RTT4.tt範本,對應的類別名稱即為RTT4),如此便可在程式碼中建立範本物件RTT4,呼叫其.TransformText()方法就能獲得轉換結果。
使用Runtime T4時,呼叫端常需傳遞參數物件給T4範本,以達到動態變更產生內容的彈性。要實現此點,可透過T4類別為partial class的特性,在專案另外加入類別名稱相同partial class檔案(即上圖中的RTT4Code.cs),於其中另外定義內部欄位、屬性,並透過建構式接收參數。
範例先要定義文章資料物件:
publicclass Post
{
publicstring Date;
publicstring Author;
publicstring Subject;
}
接著在partial class加入接收List<Post>的建構式,並宣告一個內部欄位posts,用以儲存文章清單。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace RuntimeT4
{
partialclass RTT4
{
private List<Post> posts;
public RTT4(List<Post> posts)
{
this.posts = posts;
}
}
}
T4範本如下,注意其中的this.posts,就是我們在partial class中所定義的內部欄位。
<#@ template language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#
int i = 1;
foreach (var p in this.posts)
{ #>
<#= i++ #>.<#= p.Date #> / <#= p.Author #> / <#= p.Subject #>
<# } #>
呼叫端程式如下(結果抓資料部分比主體多出許多 orz),它會抓回PTT笨版第一頁,從HTML取出文章列表交由RTT4範本執行,.TransformText()後可得到一個字串,即為產出結果。
class Program
{
staticvoid Main(string[] args)
{
//連上PTT網站取回文章資料
List<Post> posts = GetPTTPosts();
//將文章資料當成參數傳給T4
RTT4 t = new RTT4(posts);
//執行TransformText()即可取得結果
Console.WriteLine(t.TransformText());
Console.Read();
}
//取得PTT文章清單
static List<Post> GetPTTPosts()
{
WebClient wc = new WebClient();
wc.Encoding = Encoding.GetEncoding(950);
string h = wc.DownloadString(
"http://www.ptt.cc/bbs/StupidClown/index1.html");
List<Post> posts = new List<Post>();
string date = null, author = null, subject = null;
Regex re = new Regex(">(?<t>[^<]+?)<");
Func<string, string> getInnerText =
s => re.Match(s).Groups["t"].Value;
foreach (string line in h.Split('\n'))
{
if (line.StartsWith("<td width=\"50\">"))
date = getInnerText(line);
elseif (line.StartsWith("<td width=\"120\">"))
author = getInnerText(line);
elseif (line.StartsWith("<td width=\"500\">"))
{
subject = getInnerText(line);
posts.Add(new Post()
{
Date = date,
Author = author,
Subject = subject
});
}
}
return posts;
}
執行結果:
1. 4/23 / vivianJ / 我大概是唯一一個被"阿"過的女生吧 2. 4/25 / purinfocus / 小護士回憶錄2 3. 4/27 / EchoMary / 話說今天期中考.. 4. 5/05 / wubaiwife / 是任性還是笨﹖ 5. 5/22 / m0630821 / [閒聊] 油表上的E是? 6. 6/13 / hohoholalala / 去郵局 7. 6/22 / cynthia730 / 語無倫次 8. 6/22 / chachalee / 你真的是我媽嗎? 9. 6/25 / superlubu / [動畫] 三人成虎大鬧台北第七集 慘劇 10. 8/04 / keikolin / [耍笨] 我終於當媽了 11. 8/05 / utou / [動畫]笨蛋的故事 12. 9/12 / okaoka0709 / [轉錄]Re: [火大] 我的姓真的很特別嗎 13. 9/25 / Liska / [童年] 中秋節烤肉= =||| 14.10/03 / JKH945 / [生日+笨夢] 19歲最後一個夢 15.10/28 / winniechi / [耍笨] 類疊的用法 16.11/10 / olivetrees / 我弟應該算是最早的詐騙集團 17.11/10 / keikoYAMADA / 古蹟修護系 18.12/12 / glenmarlboro / 突然想到一件笨事 19.12/14 / QQQWERT / [耍笨] 其實...應該很多人做過這種事 20.12/18 / Chucky9527 / [耍笨] 我的朋友的褲子拉鍊忘了關
學會這招,就能在自己的程式中輕鬆使用T4生Code,以後就不必用StringBuilder硬兜囉!
本日口號: T4好威呀!