WuffProjects.CodeGeneration





5.00/5 (14投票s)
WuffProject.CodeGeneration 是一个易于使用、可靠且功能强大的代码生成框架
引言
WuffProjects.CodeGeneration
是一个非常基础的代码生成框架。目前,它仅用于生成 C# 代码。
它目前处于 Beta 阶段,可以下载。软件包在此文章中提供。本文旨在展示其工作原理和所需内容。对我来说,收集有关如何改进以及从您的角度来看它是否有用的反馈非常重要。
背景
如今,许多开发人员面临着庞大的数据模型,这导致了大量的模型类,它们基本上看起来都一样。因此,通常使用 T4 模板从EntityFramework
模型生成类。T4 模板对 Microsoft 和 Visual Studio 的支持不佳,并且在经过一些修改迭代后,其中许多看起来一团糟。
为了解决这个问题,开发了WuffProjects.CodeGeneration
,可在您的代码生成项目或 T4 模板中使用,使您的代码生成代码清晰、更易于阅读,并使生成的代码看起来更美观。
软件包
我提供的下载包包含一些示例,展示了如何使用该代码。尽管如此,我还是想在本文中提供一些示例,让您了解其用法以及它是否适合您的软件项目。
您也可以下载 nuget 包
PM> Install-Package WuffProjects.CodeGeneration
Using the Code
现在,让我们看一些关于WuffProjects.CodeGeneration
如何使用的示例。
代码生成完全依赖于 lambda 表达式,因此结果看起来像一个巨大的代码生成树。
基础
首先,您需要实例化一个CodeFile
。
var codeFile = new CodeFile();
CodeFile
的构造函数接受一个名为“content
”的params Func<CodeFile, ICodeElement>[]
参数,因此您基本上可以将任何想要生成的内容放入其中。
让我们看看如何创建一个包含一些using
语句、一个namespace
和一个TestClass
的文件。
var codeFile = new CodeFile(
file => file.Using("System"),
file => file.Using("System.Collections.Generic"),
file => file.EmptyLine(),
file => file.Namespace("WuffProjects.TestCode",
namespaceNode => namespaceNode.Public().Static().Class("TestClass")));
CodeFile
只有一个重要方法:Render()
。
调用codeFile.Render()
将生成以下代码。
using System;
using System.Collections.Generic;
namespace WuffProjects.TestCode
{
public static class TestClass
{
}
}
更多功能
现在让我们添加一个简单的“Add
”函数。
var codeFile = new CodeFile(
file => file.Using("System"),
file => file.Using("System.Collections.Generic"),
file => file.EmptyLine(),
file => file.Namespace("WuffProjects.TestCode",
namespaceNode => namespaceNode.Public().Static().Class("TestClass",
classNode => classNode.Function<int>("Add",
functionNode => functionNode.Parameter<int>("a"),
functionNode => functionNode.Parameter<int>("b"),
functionNode => functionNode.Free("return a + b;")))));
结果如下:
using System;
using System.Collections.Generic;
namespace WuffProjects.TestCode
{
public static class TestClass
{
Int32 Add(Int32 a, Int32 b)
{
return a + b;
}
}
}
重复生成的代码行
通常,您需要重复代码的某些部分。这可以通过Repeat
扩展方法来完成。
让我们从一个List<string>
创建一些类。
var classNames = new List<string> { "ClassA", "ClassB", "ClassC" };
var codeFile = new CodeFile(
file => file.Using("System"),
file => file.Using("System.Collections.Generic"),
file => file.EmptyLine(),
file => file.Namespace("WuffProjects.TestCode",
namespaceNode => namespaceNode.Repeat(classNames,
(repeatNode, className) => repeatNode.Public().Class(className,
classNode => classNode.Comment("Hello I am " + className)))));
如您所见,参数className
可以在Repeat()
扩展方法调用下的任何深度中使用。
您可以从任何元素使用Repeat()
,当然也可以在其他重复调用下方使用。
结果
using System;
using System.Collections.Generic;
namespace WuffProjects.TestCode
{
public class ClassA
{
// Hello I am ClassA
}
public class ClassB
{
// Hello I am ClassB
}
public class ClassC
{
// Hello I am ClassC
}
}
重复更复杂的代码
现在,让我们为测试类添加一些函数。
var functions = new[]
{
new { Name = "SetA", ParameterName = "aInput",
Comments = new List<string> {"a", "b", "c"} },
new { Name = "SetB", ParameterName = "bInput",
Comments = new List<string> {"d", "e", "f"} },
new { Name = "SetC", ParameterName = "cInput",
Comments = new List<string> {"g", "h", "i"} }
};
var codeFile = new CodeFile
(
file => file.Using("System"),
file => file.EmptyLine(),
file => file.Namespace("WuffProjects.TestCode",
namespaceNode => namespaceNode.Public().Class("MyClass",
classNode => classNode.Repeat(functions,
(repeatFunctionsNode, function) => repeatFunctionsNode.Public().Function
("void", function.Name,
functionNode => functionNode.Parameter<int>(function.ParameterName),
functionNode => functionNode.Repeat(function.Comments,
(repeatComments, comment) => repeatComments.Comment(comment)))))));
之前的代码将生成如下内容:
using System;
namespace WuffProjects.TestCode
{
public class MyClass
{
public void SetA(Int32 aInput)
{
// a
// b
// c
}
public void SetB(Int32 bInput)
{
// d
// e
// f
}
public void SetC(Int32 cInput)
{
// g
// h
// i
}
}
}
看看我们为了生成这些代码做了什么。
我们有一个匿名对象数组,其中包含具有Name
、ParameterName
和评论List<string>
的对象。您可以看到评论已添加到它们应该被添加到的函数中。
有了这个强大的功能,您可以从任何类型的对象模型创建代码。您无需将信息转换为任何其他格式。只需使用它们来生成您的代码!
"深入树状结构"
为了使代码更具结构化,避免出现无人能够维护的庞大代码树,您可以将代码的不同部分分离到其他函数中。
看看它是如何工作的
List<string> memberNames = new List<string>
{ "FirstName", "LastName", "City", "Phone" };
var codeFile = new CodeFile
(
file => file.Using("System"),
file => file.EmptyLine(),
file => file.Namespace("WuffProjects.TestCode",
namespaceNode => namespaceNode.Public().Static().Class("TestClass",
GetFunctions(memberNames).ToArray())));
private static IEnumerable<Func<Class, ICodeElement>> GetFunctions(List<string> memberNames)
{
foreach (var memberName in memberNames)
yield return parent => parent.Private().Static().Function<string>("Get" + memberName,
functionNode => functionNode.Comment("Returns the value of the member m_" + memberName),
functionNode => functionNode.Free("return m_" + memberName + ";"));
}
结果
using System;
namespace WuffProjects.TestCode
{
public static class TestClass
{
private static String GetFirstName()
{
// Returns the value of the member m_FirstName
return m_FirstName;
}
private static String GetLastName()
{
// Returns the value of the member m_LastName
return m_LastName;
}
private static String GetCity()
{
// Returns the value of the member m_City
return m_City;
}
private static String GetPhone()
{
// Returns the value of the member m_Phone
return m_Phone;
}
}
}
这有什么神奇之处?
- 该框架为几乎所有您可能想要生成的代码结构都提供了模板。
- 您无需担心缩进,结果始终格式良好。
- 您无需担心任何
string
操作,一切都已为您完成。 - 类型可以作为类型参数传递,因此您无需将它们写成
string
。 - 分层验证检查:您只能在允许存在的类或函数位置添加类或函数。
Free()
扩展方法允许您在文件的任何位置添加任何自由代码片段。- 您可以重复代码片段,并使用自己的
datastructure
作为信息源。
代码元素
当前已实现以下代码元素,可以通过一个或多个不同的扩展方法创建:
Basic
- 命名空间
- Using
- 类
- 结构
- 接口
- 属性签名
- 函数签名
- 枚举
- 属性
- Get
- Set
- 变量
- 函数
- 构造函数
- Attribute
- 事件
- 委托
- 继承(类)
- 实现(类、结构体和接口的接口实现)
修饰符
- 访问修饰符
- Public
- Private
- 内部
- Protected
- 修饰符
- 静态、抽象、常量、密封、部分、重写、新等……
操作
- Assign
- 增量
- 递减
语句
- 迭代语句
- While
- Do
- Foreach
- 跳转语句
- Break
- Continue
- 返回
- 选择语句
- If 块
- Switch
组织者
- 注释
- 空行
- 无(实际上不会渲染任何内容)
- 自由(您可以添加任何自由代码片段)
- 重复(为给定的可枚举对象重复内部代码)
历史
- 2015-04-03:添加了文章
- 2015-04-07:添加了最新版本的开发包
- 2015-04-09:添加了“代码元素”部分
- 2015-04-21:添加了 1.2.0 版本中发布的新代码元素