T4随记
关于T4模板的信息我就不赘述了,百度一大堆
MSDN的介绍 https://msdn.microsoft.com/zh-cn/library/bb126478.aspx
下面是简单的一个示例,从类中获取字符串数组,并生成数组相应的类文件
需要一个MultipleOutputHelper.ttinclude文件,你也可以百度下载,
新建一个模板文件,重命名为MultipleOutputHelper.ttinclude,然后写入如下内容,不需要任何修改
<#@ assembly name="System.Core"
#><#@ assembly name="System.Data.Linq"
#><#@ assembly name="EnvDTE"
#><#@ assembly name="System.Xml"
#><#@ assembly name="System.Xml.Linq"
#><#@ import namespace="System.Collections.Generic"
#><#@ import namespace="System.IO"
#><#@ import namespace="System.Text"
#><#@ import namespace="Microsoft.VisualStudio.TextTemplating"
#><#+
// https://raw.github.com/damieng/DamienGKit
// http://damieng.com/blog/2009/11/06/multiple-outputs-from-t4-made-easy-revisited // Manager class records the various blocks so it can split them up
class Manager {
private class Block {
public String Name;
public int Start, Length;
public bool IncludeInDefault;
} private Block currentBlock;
private readonly List<Block> files = new List<Block>();
private readonly Block footer = new Block();
private readonly Block header = new Block();
private readonly ITextTemplatingEngineHost host;
private readonly StringBuilder template;
protected readonly List<String> generatedFileNames = new List<String>(); public static Manager Create(ITextTemplatingEngineHost host, StringBuilder template) {
return (host is IServiceProvider) ? new VSManager(host, template) : new Manager(host, template);
} public void StartNewFile(String name) {
if (name == null)
throw new ArgumentNullException("name");
CurrentBlock = new Block { Name = name };
} public void StartFooter(bool includeInDefault = true) {
CurrentBlock = footer;
footer.IncludeInDefault = includeInDefault;
} public void StartHeader(bool includeInDefault = true) {
CurrentBlock = header;
header.IncludeInDefault = includeInDefault;
} public void EndBlock() {
if (CurrentBlock == null)
return;
CurrentBlock.Length = template.Length - CurrentBlock.Start;
if (CurrentBlock != header && CurrentBlock != footer)
files.Add(CurrentBlock);
currentBlock = null;
} public virtual void Process(bool split, bool sync = true) {
if (split) {
EndBlock();
String headerText = template.ToString(header.Start, header.Length);
String footerText = template.ToString(footer.Start, footer.Length);
String outputPath = Path.GetDirectoryName(host.TemplateFile);
files.Reverse();
if (!footer.IncludeInDefault)
template.Remove(footer.Start, footer.Length);
foreach(Block block in files) {
String fileName = Path.Combine(outputPath, block.Name);
String content = headerText + template.ToString(block.Start, block.Length) + footerText;
generatedFileNames.Add(fileName);
CreateFile(fileName, content);
template.Remove(block.Start, block.Length);
}
if (!header.IncludeInDefault)
template.Remove(header.Start, header.Length);
}
} protected virtual void CreateFile(String fileName, String content) {
if (IsFileContentDifferent(fileName, content))
File.WriteAllText(fileName, content);
} public virtual String GetCustomToolNamespace(String fileName) {
return null;
} public virtual String DefaultProjectNamespace {
get { return null; }
} protected bool IsFileContentDifferent(String fileName, String newContent) {
return !(File.Exists(fileName) && File.ReadAllText(fileName) == newContent);
} private Manager(ITextTemplatingEngineHost host, StringBuilder template) {
this.host = host;
this.template = template;
} private Block CurrentBlock {
get { return currentBlock; }
set {
if (CurrentBlock != null)
EndBlock();
if (value != null)
value.Start = template.Length;
currentBlock = value;
}
} private class VSManager: Manager {
private readonly EnvDTE.ProjectItem templateProjectItem;
private readonly EnvDTE.DTE dte;
private readonly Action<String> checkOutAction;
private readonly Action<List<String>> projectSyncAction; public override String DefaultProjectNamespace {
get {
return templateProjectItem.ContainingProject.Properties.Item("DefaultNamespace").Value.ToString();
}
} public override String GetCustomToolNamespace(string fileName) {
return dte.Solution.FindProjectItem(fileName).Properties.Item("CustomToolNamespace").Value.ToString();
} public override void Process(bool split, bool sync) {
if (templateProjectItem.ProjectItems == null)
return;
base.Process(split, sync);
if (sync)
projectSyncAction.EndInvoke(projectSyncAction.BeginInvoke(generatedFileNames, null, null));
} protected override void CreateFile(String fileName, String content) {
if (IsFileContentDifferent(fileName, content)) {
CheckoutFileIfRequired(fileName);
File.WriteAllText(fileName, content);
}
} internal VSManager(ITextTemplatingEngineHost host, StringBuilder template)
: base(host, template) {
var hostServiceProvider = (IServiceProvider)host;
if (hostServiceProvider == null)
throw new ArgumentNullException("Could not obtain IServiceProvider");
dte = (EnvDTE.DTE) hostServiceProvider.GetService(typeof(EnvDTE.DTE));
if (dte == null)
throw new ArgumentNullException("Could not obtain DTE from host");
templateProjectItem = dte.Solution.FindProjectItem(host.TemplateFile);
checkOutAction = fileName => dte.SourceControl.CheckOutItem(fileName);
projectSyncAction = keepFileNames => ProjectSync(templateProjectItem, keepFileNames);
} private static void ProjectSync(EnvDTE.ProjectItem templateProjectItem, List<String> keepFileNames) {
var keepFileNameSet = new HashSet<String>(keepFileNames);
var projectFiles = new Dictionary<String, EnvDTE.ProjectItem>();
var originalFilePrefix = Path.GetFileNameWithoutExtension(templateProjectItem.FileNames[]) + ".";
foreach (EnvDTE.ProjectItem projectItem in templateProjectItem.ProjectItems)
projectFiles.Add(projectItem.FileNames[], projectItem); // Remove unused items from the project
foreach (var pair in projectFiles)
if (!keepFileNames.Contains(pair.Key) && !(Path.GetFileNameWithoutExtension(pair.Key) + ".").StartsWith(originalFilePrefix))
pair.Value.Delete(); // Add missing files to the project
foreach(String fileName in keepFileNameSet)
if (!projectFiles.ContainsKey(fileName))
templateProjectItem.ProjectItems.AddFromFile(fileName);
} private void CheckoutFileIfRequired(String fileName) {
var sc = dte.SourceControl;
if (sc != null && sc.IsItemUnderSCC(fileName) && !sc.IsItemCheckedOut(fileName))
checkOutAction.EndInvoke(checkOutAction.BeginInvoke(fileName, null, null));
}
}
} #>
新建一个类TestList,内容如下
public class TestList
{
public List<string> GetList()
{
return new List<string>() { "Name1","Name2","Name3"};
}
}
然后新建一个模板文件,model.tt,写入内容如下
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="$(TargetPath)" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="TestAutoFac.Bll"#>
<#@include file="../MultipleOutputHelper.ttinclude"#> <# var manager = Manager.Create(Host, GenerationEnvironment);
TestList tl=new TestList();
List<string> lst=tl.GetList();foreach (var item in lst)
{
manager.StartNewFile(item+".cs");
//下面生成代码
#> using System;
public class <#=item#>
{
public <#=item#>()
{}
} <#
//上面生成代码
manager.EndBlock();
}
manager.Process(true);
#>
解释:<#@ assembly name="$(TargetPath)" #> 引入dll文件,这里用了vs的宏,指的是当前项目的dll
<#@include file="../MultipleOutputHelper.ttinclude"#> :引入根目录下的MultipleOutputHelper.ttinclude
<#@ import namespace="TestAutoFac.Bll"#>:引入TestList的命名空间
剩下的就没有说明了 ,看下项目的结构
红色划掉的是没用的
看下Name1.cs
大概就这样了 ,记录下 备忘
T4随记的更多相关文章
- 2017NOIP游记
记得去年这个时候,大概刚接触OI.没想到时间这么快,第一次2017NOIP之旅已经结束.初测成绩出来了,100+100+95+50=345,有浙江三十几名(@Cptraser 机房370大佬).总体感 ...
- THUWC2019 GG记
所以要什么时候才不会出现像这样的滚粗记呢? Day-?~Day-4 我也不清楚具体干了什么 不过学了很多东西,至少相对于去年还是个小菜鸡,今年已经变成大菜鸡了 Day-3~Day-1 几乎就是复习前面 ...
- NOIP2018凉凉记
首先声明一下,我去不了TG,因为我太菜了GD的某教授把我打到PJ了…… 虽然就我这实力普及都不一定1= 不管了,这次是普及,就当扎实基础了.反正HF的 $\text{hhz}$ 也考PJ,见个面多好啊 ...
- NOIP 2017 游(划水)记
Day 0 上午,大概做了一套(大)信(水)心题. 让我想想我题目都是些什么鬼.. T1:大水题.什么sort一下就过了.据说lemon上用map不会被卡常(lemon上评测,程序跑得蜜汁快). T2 ...
- NOIP2018 退役记
NOIP挂完,OI再见 AFO Day 0 早上的高铁,1点多到广州,2点多到酒店,下午就是颓颓颓,然后晚上随便刷了一下板子,反正PJ也没啥板子可以刷 就这样浪费了一天,我到底在干嘛 Day 1 早上 ...
- GDOI2018滚粗记
day-50: 高中全体成员去了北京训练,我被虐成傻逼(貌似总分全校倒数第2). day-20: 回广州了,间断式略微考好55555..... day0: 早上起床好像有点晚qwq 然后简单打了个FF ...
- NOIp2018 pj 滚粗记
NOIp2018 pj 滚粗记 考前 一个午觉睡完就到了考场 考中 \(T1\)水题切了 \(T2\)水题切了 \(T3\)好像是\(dp\),不会,先跳 \(T4\)像树上莫队一样,然后再欧拉序上面 ...
- noip2017普及 兔纸游玩记
初中的最后一场比赛...就这样结束了吧...QAQ时间...真够快的qwq 应该是初中的最后一篇游记了吧,尽量写多点... 这是一篇,初三 老年菜兔的 noip2017 普及游玩记吧! DAY 0 ...
- GDOI2017 五一游玩记
GDOI2017 到辣! 在五一比赛,成功躲了两天文化课. Day 0 早上睡到挺晚,想着同学在上课,我在睡觉,暗爽... 动车上,拿起电脑就是颓废,打模板!(然而真相是打了两个模板就开始颓了) 一天 ...
随机推荐
- Swagger 专题
随着互联网技术的发展,现在的网站架构基本都由原来的后端渲染,变成了:前端渲染.前后端分离的形态,而且前端和后端在各自的技术道路上越走越远. 前端和后端的唯一联系,变成了API接口:API文档成了前后端 ...
- solr 7.x 配置ikanalyzer
一.使用支持高版本的ikanalzyer进行分词配置(尾部有文件链接) ikanalyzer最后更新是在2012年,对于高版本的lucee不支持.但网上还是有被修改过的Ikanalyzer的6.5.0 ...
- window.url.createobjecturl 兼容多种浏览器(IE,google,360,Safari,firefox)
<script type="text/javascript"> function setImagePreview() { var docObj = document.g ...
- 【心情】CF
感觉CF上最后做出来的人数1500左右的题就是我的智商极限了. 去找那样的题练练吧.
- Oracle BI Publisher 企业版安装后的配置(BI Publisher Enterprise Edition)
Oracle BI Publisher 企业版安装后的配置(BI Publisher Enterprise Edition) (版权声明,本人原创或者翻译的文章如需转载,如转载用于个人学习,请注明出处 ...
- CUDA一维纹理内存
纹理一词来源于GPU图形世界,GPU通用并行计算"盗用"了纹理一词,定义了一个纹理内存的概念.纹理内存缓存在 设备上,在某些情况下能减少对内存的请求并降低内存带宽的使用,是专门为那 ...
- WPF模拟Office2010文件菜单的TabControl模板
原文:WPF模拟Office2010文件菜单的TabControl模板 这是Office2010中的文件菜单点开后的效果.本文我将以强大的WPF(www.itstrike.cn)来实现类似的效果.希望 ...
- POJ3723 Conscription 【并检查集合】
Conscription Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 8071 Accepted: 2810 Desc ...
- 用SendNotifyMessage代替PostMessage避免消息丢失(WIN7下消息队列的默认长度是10000,队列满后消息将被丢弃)
大家都知道PostMessage会丢消息,但是消息队列的大小是多少呢,下面做了一个测试. 代码: 1 unit Unit1; 2 3 interface 4 5 uses 6 Windows, M ...
- BZOJ 3594 Scoi2014 方波波麦田 树阵
标题效果:给定一个序列,能够选择k次每个部分的数量和在范围内+1,寻求操作后LIS最大值 我的做法是不是一个标准的解决方案. ..5E为什么跑飞的复杂性. . . 首先,显而易见的结论是,我们选择k右 ...