65.9K
CodeProject 正在变化。 阅读更多。
Home

WuffProjects.CodeGeneration

starIconstarIconstarIconstarIconstarIcon

5.00/5 (14投票s)

2015 年 4 月 7 日

CPOL

4分钟阅读

viewsIcon

21631

downloadIcon

282

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
        }
    }
}

看看我们为了生成这些代码做了什么。

我们有一个匿名对象数组,其中包含具有NameParameterName和评论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 版本中发布的新代码元素
© . All rights reserved.