有没有办法在设计时进行反射预编译?
我的意图是使用 T4 根据实现某些接口的类来生成自定义代码。我知道我可以调用反射,但我希望 T4 脚本在编译之前吐出额外的代码,否则我需要编译代码两次,一次生成 dll,两次让 T4 反映之前生成的 dll 并添加额外的脚手架。
有没有办法在设计时进行反思?
有一个更好的方法吗?
有没有办法在设计时进行反射预编译?
我的意图是使用 T4 根据实现某些接口的类来生成自定义代码。我知道我可以调用反射,但我希望 T4 脚本在编译之前吐出额外的代码,否则我需要编译代码两次,一次生成 dll,两次让 T4 反映之前生成的 dll 并添加额外的脚手架。
有没有办法在设计时进行反思?
有一个更好的方法吗?
实际上有一种基于Visual Studio Automation提供的 CodeModel 生成代码预构建的方法:项目接口提供了一个属性“CodeModel”,其中包含该项目中所有模型工件的图表。您可能希望遍历它以查找生成输出代码的类、接口、属性……。
dandrejw 已经提到了有形 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<#
}
}
#>
将您的输出代码放在代码片段显示“在此处呈现您的代码”的位置。
对于没有心情尝试让 T4 VisualStudioHelper 模板正常工作的任何未来读者,下面是一个独立的模板,它枚举了当前项目中的所有类。它在 Visual Studio 2013 中进行了测试,并受到T4 站点上的代码的启发
<#@ 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);
}
}
#>
我知道这样做的唯一方法是利用一些代码解析功能。我想不出办法来做到这一点。我确信.NET 有一些实用程序可以做到这一点。
我不确定你的情况是什么,但通常不是阅读代码,而是你有一些集中的信息,无论是 XML 文件还是一些用于从中生成代码的 UML 图(甚至是类图)。它简化了一些事情,使更改变得更容易,并让代码生成吐出更改。查看 Visual Studio 的有形 T4 工具。