在软件开发过程中,开发者经常需要处理大量相似的数据模型类。为了解决这一问题,T4模板被广泛用于从EntityFramework模型生成类。然而,T4模板的支持并不理想,且在Visual Studio中经常显得混乱。为了解决这一问题,WuffProjects.CodeGeneration应运而生,旨在使代码生成过程更加清晰、易读,并生成美观的代码。
随着数据模型的复杂性增加,开发者面临着需要生成大量相似模型类的挑战。T4模板虽然是一种解决方案,但其支持度不高,且在多次迭代后代码结构容易混乱。WuffProjects.CodeGeneration正是为了解决这一问题而开发的。
WuffProjects.CodeGeneration目前处于测试阶段,可以通过以下命令从NuGet下载:
PM> Install-Package WuffProjects.CodeGeneration
下载的包中包含了一些示例,展示了如何使用该工具。本文将进一步提供一些示例,帮助读者理解其用法,并判断它是否适合软件项目。
WuffProjects.CodeGeneration完全依赖于lambda表达式,生成的代码看起来像一个庞大的代码生成树。首先,需要实例化一个CodeFile对象:
var codeFile = new CodeFile();
CodeFile的构造函数接受一个params Func<CodeFile, ICodeElement>[]参数,名为"content",基本上可以将任何想要生成的内容放入其中。
下面是一个创建包含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()方法将生成以下代码:
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()扩展调用下的任何深度使用。
现在让为测试类添加一些函数:
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
}
}
}
为了使代码结构更加清晰,避免生成一个无法维护的庞大代码树,可以将代码的某些部分分离到其他函数中。下面是一个示例:
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;
}
}
}
该框架为几乎所有可能想要生成的代码结构提供了模板。不需要关心缩进,结果总是格式良好的。不需要关心任何字符串操作,它都为完成了。类型可以作为类型参数传递,所以不需要将它们写成字符串。