有沒有辦法做反射預編譯 - 在設計時?設計時間反射
我的意圖是利用T4吐出基礎上實現特定接口的類的自定義代碼。我知道我可以調用反射,但我希望T4腳本在編譯之前吐出額外的代碼,否則我需要編譯兩次代碼,一次生成dll,兩次讓T4反映先前生成的dll並添加額外的腳手架。
有沒有辦法在設計時做的寫照嗎?
有沒有更好的方式來做到這一點?
有沒有辦法做反射預編譯 - 在設計時?設計時間反射
我的意圖是利用T4吐出基礎上實現特定接口的類的自定義代碼。我知道我可以調用反射,但我希望T4腳本在編譯之前吐出額外的代碼,否則我需要編譯兩次代碼,一次生成dll,兩次讓T4反映先前生成的dll並添加額外的腳手架。
有沒有辦法在設計時做的寫照嗎?
有沒有更好的方式來做到這一點?
實際上有一種方法可以根據Visual Studio Automation提供的CodeModel預生成代碼:Project Interface提供了一個屬性「CodeModel」,其中包含該項目中所有模型構件的圖形。您可能想要遍歷它以找到類,接口,屬性,...,根據它們生成您的輸出代碼。
dandrejw已經提到的Tangible T4-Editor。它有一個免費的模板庫。有一個可重複使用的模板「有形的Visual Studio自動化助手」,對你的情況應該非常有幫助。使用這個模板,你可以解決您的問題是這樣的:
這是T4模板檢測執行INotifyPropertyChanged的所有類中的代碼。
<#
// get a reference to the project of this t4 template
var project = VisualStudioHelper.CurrentProject;
// get all class items from the code model
var allClasses = VisualStudioHelper.GetAllCodeElementsOfType(project.CodeModel.CodeElements, EnvDTE.vsCMElement.vsCMElementClass, false);
// iterate all classes
foreach(EnvDTE.CodeClass codeClass in allClasses)
{
// get all interfaces implemented by this class
var allInterfaces = VisualStudioHelper.GetAllCodeElementsOfType(codeClass.ImplementedInterfaces, EnvDTE.vsCMElement.vsCMElementInterface, true);
if (allInterfaces.OfType<EnvDTE.CodeInterface>()
.Any(i => i.Name == "INotifyPropertyChanged"))
{
#>Render your code here<#
}
}
#>
將您的輸出代碼放在代碼片段所示的「在此處呈現您的代碼」。
我知道做到這一點的唯一方法是使用一些代碼解析能力。 我想不出一個辦法解決我的頭如何做到這一點。我相信.NET有一些可以做到的實用工具。
我不知道你是什麼情況,但通常不是讀碼,你有一些信息的集中一塊,無論是一個XML文件或一些UML圖(類圖偶數),用於從生成代碼。它簡化了一些事情,並使更容易進行更改並讓代碼生成將變化吐出。 查看適用於Visual Studio的Tangible T4工具。
呃 - 這將是醜陋的:(希望一些巧妙的把戲 – Alwyn
對於任何未來的讀者而言,都沒有試圖讓T4 VisualStudioHelper模板工作,下面是一個自包含的模板,它列舉了當前項目中的所有類。這是在Visual Studio 2013測試,並在T4 Site
<#@ template debug="true" hostSpecific="true" #>
<#@ output extension=".cs" #>
<#@ Assembly Name="System.Core" #>
<#@ assembly name="EnvDte" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#
foreach(var ns in GetNamespaceElements())
{
foreach(var cc in ns.Members.OfType<EnvDTE.CodeClass>())
{
#>Render your code here<#
}
}
#>
<#+
public IEnumerable<EnvDTE.CodeNamespace> GetNamespaceElements()
{
var visualStudio = (this.Host as IServiceProvider).GetService(typeof(EnvDTE.DTE))
as EnvDTE.DTE;
var project = visualStudio.Solution.FindProjectItem(this.Host.TemplateFile)
.ContainingProject as EnvDTE.Project;
var projItems = new List<EnvDTE.ProjectItem>();
FillProjectItems(project.ProjectItems, projItems);
var names = new HashSet<string>(projItems
.Where(i => i.FileCodeModel != null)
.SelectMany(i => i.FileCodeModel.CodeElements.OfType<EnvDTE.CodeElement>())
.Where(e => e.Kind == EnvDTE.vsCMElement.vsCMElementNamespace)
.Select(e => e.FullName));
var codeNs = new List<EnvDTE.CodeNamespace>();
FillCodeNamespaces(project.CodeModel.CodeElements.OfType<EnvDTE.CodeNamespace>(), codeNs);
return codeNs.Where(ns => names.Contains(ns.FullName));
}
public void FillCodeNamespaces(IEnumerable<EnvDTE.CodeNamespace> parents, List<EnvDTE.CodeNamespace> all)
{
foreach (var parent in parents)
{
all.Add(parent);
FillCodeNamespaces(parent.Members.OfType<EnvDTE.CodeNamespace>(), all);
}
}
public void FillProjectItems(EnvDTE.ProjectItems items, List<EnvDTE.ProjectItem> ret)
{
if (items == null) return;
foreach(EnvDTE.ProjectItem item in items)
{
ret.Add(item);
FillProjectItems(item.ProjectItems, ret);
}
}
#>
靈感來自代碼是在同一個項目的模板和類?我只是在猜測,但如果他們在不同的項目中,也許你可以更好地控制訂單,所以可以在另一個項目之後進行編譯。 – Kobi
是的,我也考慮過這個,但是你最終還是會逐個構建你的項目,除非有辦法停止構建,運行t4,取消暫停。 – Alwyn
如果模板生成是其中的一部分,則不必停止構建:[生成過程中的代碼生成](http://msdn.microsoft.com/zh-cn/library/ee847423.aspx)。我從來沒有這樣做過,也不確定模板引擎在哪個上下文中運行,但它看起來可以工作。 – Kobi