Introduction
WuffProjects.CodeGeneration
is a very basic code generation framework. It is, for now, meant to generate only C# code with.
It is now in beta state and can be downloaded. The package is to this article. This article is meant to show how it works and what is required. It is very important for me to collect feedback about what can be improved and if it's useful from your point of view.
Background
Today, many developers are confronted with great datamodels which result in a lot of model classes which are all basically looking the same. Therefore, often T4 templates are used to generate classes out of EntityFramework
models. T4 templates are not very good supported by Microsoft and Visual Studio and many of them look like a big mess after some iterations of changes.
To go around that, WuffProjects.CodeGeneration
was developed to be used inside your code generation project or T4 template to make your code generation code look clear and make it much better to read and also make your generated code look nice.
The Package
The package which I provided for download contains a few samples that show you how to use the code. Nevertheless, I want to give some samples in this article, to give you an idea of how it can be used and if it is the right thing for your software project.
You can also download the nuget package:
PM> Install-Package WuffProjects.CodeGeneration
Using the Code
Now for some samples of how WuffProjects.CodeGeneration
is used.
The code generation depends completely on lambdas so the result looks like a big code generation tree.
Basics
First of all, you need to instance a CodeFile
.
var codeFile = new CodeFile();
The constructor of CodeFile
takes a params Func<CodeFile, ICodeElement>[]
named "content
", so you can basically put everything you want to generate in here.
Let's see how to create a file with some using
s, a namespace
and a 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")));
The CodeFile
has only one important method: Render()
.
Calling codeFile.Render()
results in the following generated code:
using System;
using System.Collections.Generic;
namespace WuffProjects.TestCode
{
public static class TestClass
{
}
}
More Features
Now let's add a simple "Add
" Function:
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;")))));
The result looks like this:
using System;
using System.Collections.Generic;
namespace WuffProjects.TestCode
{
public static class TestClass
{
Int32 Add(Int32 a, Int32 b)
{
return a + b;
}
}
}
Repeat lines of generated code
Normally, you want to repeat parts of the code. That can be done with the Repeat
extension.
Let's create some classes from a 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)))));
As you can see, the parameter className
can be used at any depth below the Repeat()
extension call.
You can use Repeat()
from any element and certainly below other repeats.
The result:
using System;
using System.Collections.Generic;
namespace WuffProjects.TestCode
{
public class ClassA
{
}
public class ClassB
{
}
public class ClassC
{
}
}
Repeat more complex code
Let's now add some functions to a test class:
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)))))));
The previous code results in:
using System;
namespace WuffProjects.TestCode
{
public class MyClass
{
public void SetA(Int32 aInput)
{
}
public void SetB(Int32 bInput)
{
}
public void SetC(Int32 cInput)
{
}
}
}
Take a look at what we took to generate that code.
We had an anonymous object array containing objects with a Name
, ParameterName
and a List<string>
of comments. You can see that the comments are added in the functions they were meant to.
With that power, you can create code from any kind of object model. You don't have to convert your information into anything. Just use them and generate your code!
"Get down the tree"
To get more structure into the code and not end up with a giant code tree which no one can maintain, you can separate parts of the code into other functions.
See how it works:
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 + ";"));
}
The result:
using System;
namespace WuffProjects.TestCode
{
public static class TestClass
{
private static String GetFirstName()
{
return m_FirstName;
}
private static String GetLastName()
{
return m_LastName;
}
private static String GetCity()
{
return m_City;
}
private static String GetPhone()
{
return m_Phone;
}
}
}
What's Amazing About That?
- The framework provides a template for nearly all code structures you might want to generate.
- You don't have to care about indentation, the result is always well formatted.
- You don't have to care about any
string
operations, it's all done for you. - Types can be passed as type parameter so you don't have to write them as
string
s. - Hierarchical validation checking: You can only add a class or a function where it is allowed to exist.
- The
Free()
extension allows you to add any free code snippet at any place of your file. - You can repeat code snippets and use your own
datastructure
as information source.
The Code Elements
The following code elements are currently implemented and can be created by one or more different extension methods:
Basic
- Namespace
- Using
- Class
- Struct
- Interface
- Property signature
- Function signature
- Enum
- Property
- Variable
- Function
- Constructor
- Attribute
- Event
- Delegate
- Extends (class)
- Implements (Implementation of interfaces for classes, structs and interfaces)
Modifier
- Access modifier
- Public
- Private
- Internal
- Protected
- Modifier
- Static, abstract, const, sealed, partial, override, new, ……
Operations
- Assign
- Increment
- Decrement
Statements
- Iteration statements
- Jump statements
- Selection statements
Organizer
- Comment
- Empty line
- Nothing (that really renders to nothing)
- Free (any free code snippet you like to add)
- Repeat (repeats the inside code for a given enumerable)
History
- 2015-04-03: Added the article
- 2015-04-07: Added latest version of development package
- 2015-04-09: Added section "The Code Elements"
- 2015-04-21: Added new code elements which were released in version 1.2.0