在软件开发过程中,代码生成是一个常见的需求,它可以帮助自动化重复性的工作,提高开发效率。Genuilder.Extensibility框架提供了一种简单而强大的方法来实现这一目标。本文将详细介绍如何在Visual Studio项目中使用Genuilder.Extensibility来生成自定义代码。
如果之前使用过项目Genuilder,那么可能已经熟悉了它的强大功能。Genuilder是一个Visual Studio扩展,它使用MSBuild来执行各种任务,如配置文件检查等,而无需任何安装。Genuilder.Extensibility是Genuilder的一个新特性,它允许在编译过程中轻松生成自己的代码。不需要了解MSBuild,不需要修改项目文件,也不需要成为C#专家。只需要知道如何在Visual Studio中引用一个程序集并创建一个新的类库。
要在项目中使用Genuilder,首先需要阅读官方文档。这里简要说明一下步骤:创建一个控制台应用程序,并使用将要创建的InterfaceExtractor特性。将Genuilder的bin文件夹复制到解决方案文件夹中。然后使用Genuilder的GUI,浏览到解决方案并激活项目中的Genuilder。这样就完成了!
要创建自己的特性,需要创建一个新的类库,项目名称必须遵循FEATURE_NAME.Gen的约定,这是强制性的。在特性项目中引用Genuilder和Genuilder.Extensibility,然后创建一个新的插件。插件是一个实现了IPlugin接口的类(例如HelloWorldPlugin)。接下来,将特性项目添加到项目引用中。在Initialize方法的实现中生成一个文件。
public class HelloWorldPlugin : IPlugin
{
public void Initialize(ICodeRepository repository)
{
var code = repository.Get("GeneratedCode.gen.cs");
code.Content = "class HelloWorld{}";
}
}
编译后,将看到HelloWorld类,并且可以在IntelliSense中看到它。
如果源文件被删除,希望目标生成的代码自动从项目中删除,这可以通过使用CodeItem类的SourceOf和TargetOf方法来实现。这些方法返回一个CodeDependency对象,然后只需要注册到ShouldUpdateTarget事件即可。
public class DependencyHelloWorldPlugin : IPlugin
{
public void Initialize(ICodeRepository repository)
{
repository.CodeItemCreated += new CodeItemCreatedHandler(repository_CodeItemCreated);
}
void repository_CodeItemCreated(ICodeRepository sender, CodeItem item)
{
if (!item.Name.EndsWith("generated.cs"))
{
var dependency = item.SourceOf(item.Name + "generated.cs");
dependency.ShouldUpdateTarget += new CodeDependencyHandler(dependency_ShouldUpdateTarget);
}
}
void dependency_ShouldUpdateTarget(CodeDependency sender, CodeItem target)
{
target.Content = String.Format("public class {0}{}", ToClassName(sender.Source.Name));
}
private string ToClassName(string fileName)
{
return fileName.Replace('.', '_').Replace('/', '_').Replace('\\', '_');
}
}
可以使用NRefactory(SharpDevelop的解析器)与Genuilder集成。Genuilder.Extensibility附带了一个扩展,它将NRefactory与Genuilder集成在一起。扩展是一个以CodeItem为构造函数参数的类。在特性项目中引用Genuilder.Extensibility.NRefactory、ICSharpCode.NRefactory和ICSharpCode.SharpDevelop.Dom。然后使用CodeItem.GetExtension<CompilationUnitExtension>()来使用扩展。
public class DependencyHelloWorldPlugin2 : IPlugin
{
public void Initialize(ICodeRepository repository)
{
repository.CodeItemCreated += new CodeItemCreatedHandler(repository_CodeItemCreated);
}
void repository_CodeItemCreated(ICodeRepository sender, CodeItem item)
{
if (!item.Name.EndsWith("generated2.cs") && HasAClassWithAttribute<GenerateAttribute>(item))
{
var dependency = item.SourceOf(item.Name + "generated2.cs");
dependency.ShouldUpdateTarget += new CodeDependencyHandler(dependency_ShouldUpdateTarget);
}
}
private bool HasAClassWithAttribute<T>(CodeItem item)
{
var nRefactoryExtension = item.GetExtension<CompilationUnitExtension>();
return HasAttribute<T>(nRefactoryExtension.CompilationUnit);
}
private bool HasAttribute<T>(INode node)
{
if (node is TypeDeclaration)
{
TypeDeclaration declaration = (TypeDeclaration)node;
var hasAttribute = declaration.Attributes.SelectMany(attributes => attributes.Attributes).Any(attribute => attribute.Name.EndsWith(typeof(GenerateAttribute).Name));
if (hasAttribute)
return true;
}
foreach (var child in node.Children)
{
var hasAttribute = HasAttribute<T>(child);
if (hasAttribute)
return true;
}
return false;
}
void dependency_ShouldUpdateTarget(CodeDependency sender, CodeItem target)
{
var nRefactoryExtension = target.GetExtension<CompilationUnitExtension>();
nRefactoryExtension.CompilationUnit.Children.Clear();
nRefactoryExtension.CompilationUnit.Children.Add(new TypeDeclaration(Modifiers.Public, new List<AttributeSection>()) { Name = ToClassName(sender.Source.Name) });
nRefactoryExtension.Save();
}
private string ToClassName(string fileName)
{
return fileName.Replace('.', '_').Replace('/', '_').Replace('\\', '_') + "2";
}
}
打开特性项目的属性,然后转到调试选项卡。将启动程序设置为MSBuild(取决于.NET版本)。命令参数必须引用插件所在的项目。之后,当想要调试时,只需按F5即可。