2013-01-03 13 views
10

C'è un modo per fare la riflessione pre-compilazione - in fase di progettazione?Design Time Reflection

Il mio intento è utilizzare T4 per sputare codici personalizzati in base a classi che implementano determinate interfacce. So che posso invocare la riflessione, ma voglio che lo script T4 sputi il ​​codice aggiuntivo prima di compilare, altrimenti dovrò compilare il codice due volte, una volta per generare dll, due volte per far riflettere T4 sulla dll generata in precedenza e aggiungere impalcature aggiuntive.

C'è un modo per fare la riflessione in fase di progettazione?

C'è un modo migliore per farlo?

+0

I modelli e le classi sono nello stesso progetto? Sto solo supponendo, ma forse puoi avere un controllo migliore sull'ordine se fossero in progetti diversi, in modo che uno possa compilare dopo l'altro. – Kobi

+0

Sì, ci ho pensato anche io, ma finisci comunque per costruire il tuo progetto uno per uno, a meno che non ci sia un modo per fermare la compilazione, eseguire t4, interrompere la pausa. – Alwyn

+1

Non è necessario interrompere la generazione se la generazione del modello è una parte di essa: [Generazione del codice in un processo di creazione] (http://msdn.microsoft.com/en-us/library/ee847423.aspx). Non l'ho mai fatto, e non sono sicuro in quale contesto il motore dei template funziona, ma sembra che funzioni. – Kobi

risposta

21

Esiste effettivamente un modo per generare la pre-generazione del codice basato sul CodeModel fornito da Visual Studio Automation: L'interfaccia di progetto fornisce una proprietà "CodeModel" che contiene un grafico di tutte le risorse utente del modello in quel progetto. Potresti voler attraversarlo per trovare classi, interfacce, proprietà, ... in base alle quali generi il tuo codice di uscita.

dandrejw già menzionato il Tangible T4-Editor. Ha una galleria di modelli gratuita. È disponibile un modello riutilizzabile "Assistente di automazione di Visual Studio tangibile" che dovrebbe essere estremamente utile nel tuo caso. Utilizzando questo modello è possibile risolvere il problema in questo modo:

Questo è il codice all'interno di un modello t4 che rileva tutte le classi che implementano 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<# 
     } 
    } 
#> 

Inserire il codice di uscita in cui il frammento di codice dice "Render your code here".

+0

Questo è davvero dolce! Grazie per il tuo aiuto – Alwyn

+0

Come si comporta questo senza Visual Studio (cioè su un server di build, utilizzando solo MSBuild)? – julealgon

+0

Vorrei non aspettarsi che questo codice funzioni senza Visual Studio.È possibile accedere a CodeModel EnvDTE solo quando Visual Studio è in esecuzione e fornisce l'host per il modello. Tuttavia, se hai trovato un altro modo per istanziare la logica per accedere a EnvDTE senza VS, si potrebbe ottenere questo codice per funzionare ... Mi dispiace. – Nico

0

L'unico modo che conosco per farlo è utilizzare alcune funzionalità di analisi del codice. Non riesco a pensare a un modo fuori di testa come farlo. Sono sicuro che .NET ha alcune utilità che possono farlo.

Non sono sicuro di quale sia la tua situazione, ma di solito invece di leggere il codice, hai qualche informazione centralizzata, che si tratti di un file XML o di un diagramma UML (anche diagramma di classe) utilizzato per generare codice da . Semplifica le cose un po 'e rende più facile apportare modifiche e consente alla generazione di codice di sputare le modifiche. Dai un'occhiata agli strumenti Tangible T4 per Visual Studio.

+0

Ugh - che sarà brutto :(Sperando in qualche trucco pulito – Alwyn

6

Per i futuri lettori che non hanno voglia di provare a utilizzare il modello T4 VisualStudioHelper, di seguito è riportato un modello autonomo che elenca tutte le classi del progetto corrente. È stato testato in Visual Studio 2013 ed è stato ispirato dal codice su 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); 
    } 
    } 
#>