Geeks With Blogs
Willem's... {rue if I mellow}

If you write applications in .NET that generate .NET language source-code as output, and the output language should be selectable (typically C# or VB.NET), then you will probably end up generating the output from a System.CodeDom graph. Once you have the CodeDOM the actual code generation support in .NET is easy, clean and produces reasonable code. So what's the catch? Well, consider at least the following:

  • Building CodeDOMs programmatically is a real pain - the .NET API's are convoluted, difficult to understand, error-prone and you often require many lines of code to generate one line of code in the CodeDOM. (Reminds me of the days I used to program in assembler..). For this reason, many people have gone to great pains to abstract the CodeDOM API's into more meaningful, much easier to use, and generally more efficient class libraries. I reference a few of these from CodeProject below:
  • Some of the constructs that you prefer to code yourself just cannot be reproduced in a CodeDOM, and require that you be a lot more creative and defensive in your approach when programmatically building CodeDOMs.

Another approach is to find or build a System.CodeDom.Compiler.ICodeParser implementation for the input.NET language you are generating CodeDOMs from. This allows you to write code in that .NET language, which is then parsed to produce a System.CodeDom.CodeCompileUnit (CodeDOM). Once you have the CodeDOM it is trivial to generate the code (see the CodeGeneration.GenerateCode method below). But again, there is no free-lunch - many ICodeParser implementations exist, but all of them (in my experience) are trivial solutions, mainly geared to generating code for class-interfaces or web-service interfaces.

Enter IC#Code and their #Develop (SharpDevelop) product.

As part of this product, they ship an assembly - ICSharpCode.NRefactory.dll  (and you can build the source code) which provides a fairly comprehensive ICodeParser implementation. Using the CodeDOMVisitor 'visitor' object, it is easy to generate source-code from a CodeDOM created by parsing source-code, as demonstrated in the code below. Note, though, that as before your input source-code will have to be very standard (no funny stuff), and there are some quirks with the CodeDOMVisitor object (which includes some issues with class constructor generation, and some of the looping constructs). In general the results are pleasing, and I look forward to future updates to this assembly. Interestingly, they also provide other 'visit-ations', including a working C#-to-VB.NET converter.

The code below was written for VS2005/.NET 2.0 and uses the #Develop 2.0.0.1462RC2 version source-code and DLL's. You will need to add a reference in your project to the ICSharpCode.NRefactory.dll assembly to get your project to build.

CodeGenerator Classes for .NET 2.0
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.Text;
using System.IO;
using ICSharpCode.NRefactory.Parser;
namespace CodeGeneration
{
  public class CodeGenerator
  {
    private ICodeParser parser;
    public ICodeParser Parser
    {
      get { return parser; }
      set { parser = value; }
    }
    private CodeDomProvider provider;
    public CodeDomProvider Provider
    {
      get { return provider; }
      set { provider = value; }
    }
    private CodeGeneratorOptions options;
    public CodeGeneratorOptions Options
    {
      get { return options; }
      set { options = value; }
    }
    protected virtual CodeGeneratorOptions 
      GetDefaultOptions()
    {
      CodeGeneratorOptions options = 
        new CodeGeneratorOptions();
      options.BracingStyle = "C";
      options.BlankLinesBetweenMembers = false;
      options.IndentString = "\t";
      return options;
    }
    public virtual void GenerateCode(TextReader reader, 
      TextWriter writer)
    {
      CodeCompileUnit codeCompileUnit = Parser.Parse(reader);
      Provider.GenerateCodeFromCompileUnit(codeCompileUnit, writer, Options);
      reader.Close();
      // Important to flush the output stream.
      writer.Flush();
      writer.Close();
    }
    public CodeGenerator(ICodeParser parser, 
      CodeDomProvider provider)
    {
      Options = GetDefaultOptions();
      Parser = parser;
      Provider = provider;
    }
    public CodeGenerator(ICodeParser parser, 
      CodeDomProvider provider,
      CodeGeneratorOptions options)
    {
      Options = options;
      Parser = parser;
      Provider = provider;
    }
  }
  public class Parser : ICodeParser
  {
    #region ICodeParser Members
    public CodeCompileUnit Parse(TextReader codeStream)
    {
      // Set the parser for the correct language.
      IParser parser = ParserFactory.CreateParser(Language,
        codeStream);
      parser.Parse();
      // Use a CodeDom visitor to gen. a CodeCompileUnit.
      CodeDOMVisitor visit = new CodeDOMVisitor();
      CodeNamespace globalNamespace =  (CodeNamespace)
        visit.Visit(parser.CompilationUnit, null);
      // Mostly, you need to remove the "Global"
      // code namespace from the compile unit.
      visit.codeCompileUnit.Namespaces.Remove(globalNamespace);
      return visit.codeCompileUnit;
    }
    #endregion
    private SupportedLanguage language = SupportedLanguage.CSharp;
    public SupportedLanguage Language
    {
      get { return language; }
      set { language = value; }
    }
    public Parser(SupportedLanguage language)
    {
      Language = language;
    }
  }
}
Posted on Monday, July 24, 2006 8:55 AM .NET Adventures | Back to top


Comments on this post: Generate Code-DOMs directly from C# or VB.NET

# re: Generate Code-DOMs directly from C# or VB.NET
Requesting Gravatar...
This fails in Switch case, I wonder why SharpDev is not creating multiple concateneted IF statements for the Switch Statement.
Left by Akash Kava on Oct 14, 2006 5:21 AM

Your comment:
 (will show your gravatar)


Copyright © Willem Fourie | Powered by: GeeksWithBlogs.net