Reed Robinson has been talking about the Mobile LOB Accelerator lately. This got me thinking how I could make it easier to write applications based on it. This then lead me to CodeDOM. Ok I am lying but then I can't give away all of my secrets.
So what is CodeDOM? Say you want to generate code. You could write a program or class that can output source by writing to a file. However what happens when you want to manipulate the code before writing for example renaming a class or namespace. This becomes difficult if you are just maintaining a string copy in memory. And worst what happens when now you need to output VB rather than C#. You could write a set of objects that represent parts of the code. That is what CodeDOM is a document model for source code. It is essentially a framework of objects used to generate source code or even compiled code. It can be very quirky but powerful and useful.
So first let me back up and give a simplistic view of compiling an application. From the diagram above you can see that essential a source code file is transformed by the compiler into machine instructions. Ok that sounds familar I have an XML file and it is transformed into HTML via XSLT. So what if rather than a compiler I sent the source code into a parser that returned the structure of source code as a set of objects. I then take these objects manipulate them and then compile them or output them as source code. CodeDOM achieves this by allow you to use a CodeDOMProvider to generate a compiled .NET program or source code in CSharp or VB.NET.
Further investigation into the CodeDOMProvider is that it can generate source from
- CompileUnit - a single grouping of namespaces, think source file.
- Expression - a math or logical expression.
- Member - a property, constructor, method or field in a class.
- Namespace - says it all.
- Statement - a code statement or block of code.
- Type - a type including struct, enum, class or delegate.
The CodeDOM grammer is very complex consisting of 81 objects. For this posting I will just show you have to create a simple Class and output it in VB or C#. The listing is below. The steps are
- Create the Code graph
- Output Source in C#
- Output Source in VB
I put the requirement for VB to show the true power of CodeDOM. The biggest step is the creation of the code graph. The code graph requires a CodeCompileUnit, Namespace, Class, field, property and method. I will leave you to explorer the code below and ponder why I am linking Mobile LOB and CodeDOM.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.CodeDom;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using Microsoft.VisualBasic;
using System.IO;
namespace GenerateClass
{
class Program
{
public static void GenerateSource(CodeDomProvider provider,
CodeCompileUnit codeUnit)
{
StringBuilder sourceFile;
IndentedTextWriter writer;
sourceFile = new StringBuilder(@"c:\temp\HelloWorld");
if (provider.FileExtension[0] != '.')
{
sourceFile.Append('.');
}
sourceFile.Append(provider.FileExtension);
// Create a TextWriter to a StreamWriter to the output file.
writer = new IndentedTextWriter(
new StreamWriter(sourceFile.ToString(), false), " ");
// Generate source code using the code provider.
provider.GenerateCodeFromCompileUnit(codeUnit, writer,
new CodeGeneratorOptions());
// Close the output file.
writer.Close();
}
static void Main(string[] args)
{
CodeCompileUnit codeUnit;
CodeFieldReferenceExpression fieldReference;
CodeTypeDeclaration newClass;
CodeMemberMethod newMethod;
CodeMemberField newField;
CodeMemberProperty newProperty;
CodeNamespace ns;
CodeTypeReference propertyType;
CodeDomProvider provider;
// Create Unit
codeUnit = new CodeCompileUnit();
// Create Namespace
ns = new CodeNamespace("CodeDOMTest");
ns.Imports.Add(new CodeNamespaceImport("system.IO"));
codeUnit.Namespaces.Add(ns);
// Create Class
newClass = new CodeTypeDeclaration("HelloClass");
newClass.IsClass = true;
newClass.Attributes = MemberAttributes.Public;
ns.Types.Add(newClass);
// Create Property
// Step 1. Create Type to be used by the field and property
// Step 2. Create Field
// Step 3. Create Property
// Step 4. Add Get and Set statements
propertyType = new CodeTypeReference(typeof(string));
newField = new CodeMemberField(propertyType,"_name");
newField.Attributes = MemberAttributes.Private;
newClass.Members.Add(newField);
fieldReference = new CodeFieldReferenceExpression();
fieldReference.FieldName = "_name";
newProperty = new CodeMemberProperty();
newProperty.Name = "Name";
newProperty.Attributes = MemberAttributes.Public | MemberAttributes.Final;
newProperty.Type = propertyType;
// Get statement will be return _name
newProperty.GetStatements.Add(new CodeMethodReturnStatement(fieldReference));
// Set statement will be _name = value
newProperty.SetStatements.Add(new CodeAssignStatement(fieldReference,
new CodeArgumentReferenceExpression("value")));
newClass.Members.Add(newProperty);
// Create Method
newMethod = new CodeMemberMethod();
newMethod.Name = "SayHello";
newMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
newMethod.Statements.Add(new CodeSnippetExpression(
"Console.WriteLine(\"Hello \" + Name)"));
newClass.Members.Add(newMethod);
// Write CSharp
GenerateSource(new CSharpCodeProvider(), codeUnit);
// Write VB
GenerateSource(new VBCodeProvider(), codeUnit);
}
}
}

Comments