2013-01-03 49 views
10

有沒有辦法做反射預編譯 - 在設計時?設計時間反射

我的意圖是利用T4吐出基礎上實現特定接口的類的自定義代碼。我知道我可以調用反射,但我希望T4腳本在編譯之前吐出額外的代碼,否則我需要編譯兩次代碼,一次生成dll,兩次讓T4反映先前生成的dll並添加額外的腳手架。

有沒有辦法在設計時做的寫照嗎?

有沒有更好的方式來做到這一點?

+0

靈感來自代碼是在同一個項目的模板和類?我只是在猜測,但如果他們在不同的項目中,也許你可以更好地控制訂單,所以可以在另一個項目之後進行編譯。 – Kobi

+0

是的,我也考慮過這個,但是你最終還是會逐個構建你的項目,除非有辦法停止構建,運行t4,取消暫停。 – Alwyn

+1

如果模板生成是其中的一部分,則不必停止構建:[生成過程中的代碼生成](http://msdn.microsoft.com/zh-cn/library/ee847423.aspx)。我從來沒有這樣做過,也不確定模板引擎在哪個上下文中運行,但它看起來可以工作。 – Kobi

回答

21

實際上有一種方法可以根據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<# 
     } 
    } 
#> 

將您的輸出代碼放在代碼片段所示的「在此處呈現您的代碼」。

+0

這真的很甜!謝謝你的幫助 – Alwyn

+0

這沒有Visual Studio(即在一個構建服務器上,僅使用MSBuild)的行爲? – julealgon

+0

我會不要期望這個代碼在沒有Visual Studio的情況下工作。在Visual Studio運行時只能訪問EnvDTE CodeModel,併爲模板提供主機。但是,如果您發現另一種方法來實例化訪問不帶VS的EnvDTE的邏輯,可能會得到此代碼的工作...我很抱歉。 – Nico

0

我知道做到這一點的唯一方法是使用一些代碼解析能力。 我想不出一個辦法解決我的頭如何做到這一點。我相信.NET有一些可以做到的實用工具。

我不知道你是什麼情況,但通常不是讀碼,你有一些信息的集中一塊,無論是一個XML文件或一些UML圖(類圖偶數),用於從生成代碼。它簡化了一些事情,並使更容易進行更改並讓代碼生成將變化吐出。 查看適用於Visual Studio的Tangible T4工具。

+0

呃 - 這將是醜陋的:(希望一些巧妙的把戲 – Alwyn

6

對於任何未來的讀者而言,都沒有試圖讓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); 
    } 
    } 
#>