Using CodeDom for Isolated Fragments

Frans Bouma recently posted a question to DevelopMentor's Advanced .NET mailing list. He has a requirement to generate a type string that is valid in VB.NET.

Of course, since the Type class is a CLRism, it is language-agnostic, so it returns strings that look how it wants them to. For simple types, these are just type names, and so translated into any language that presents .NET types more or less directly. (E.g. VB.NET or C#.) But for arrays, things are a little different. VB.NET isn't happy with its representation because square brackets are used: the type object for an array of integers reports its name as System.Int32[]. (C# happens to be happy with this, but there are other examples that aren't valid C# either, e.g. a one-dimensional integer array with a non-zero lower bound will count out as System.Int32[*].)

To get around this, I suggested using the CodeDom. Frans had the perfectly reasonable reaction that the CodeDom was way too complex, which, to be fair, it is. It's fairly hard work generating code this way. He didn't want to have to rewrite his code around CodeDom, which is a perfectly sane point of view.

I was about to reply saying that I agreed when my inability to resist a challenge took over. I thought to myself that surely it must be possible to write a wrapper class that tackled the CodeDom to do the necessary work, encapsulating all the complexity. This class could just present a very simple API that let you pass in a Type object, and it would return the appropriate string.

It turned out to be a whole lot simpler than I though it would be. Here it is:

using System;
using System.IO;
using System.CodeDom.Compiler;
using System.CodeDom;
using Microsoft.VisualBasic;

public class CodeUtils
{
    public static string GenVBArrayDeclaration(Type arrayType,
                                               string varName)
    {
        if (arrayType == null)
            throw new ArgumentNullException("arrayType");
        if (varName == null)
            throw new ArgumentNullException("varName");
        if (!arrayType.IsArray)
            throw new ArgumentException("Type must be an array",
                                        "arrayType");

        VBCodeProvider cp = new VBCodeProvider();
        ICodeGenerator cg = cp.CreateGenerator();

        CodeVariableDeclarationStatement stmt =
            new CodeVariableDeclarationStatement(arrayType, varName);
        using (StringWriter output = new StringWriter())
        {
            cg.GenerateCodeFromStatement(stmt, output, null);
            output.Flush();
            return output.ToString();
        }
    }
}

If you pass this the type of an array of integers, and the name myVar it will produce this output:

Dim myVar() As Integer

It surprised me how easy it was to use the CodeDom here, mostly because my previous experience with the CodeDom has involved taking the rather more all-encompasing approach of using ICodeCompiler. I hadn't used ICodeGenerator before, or maybe I had but didn't notice that it could be used to generate fragments like this. Of course it makes perfect sense that it can - it has to be able to in order to allow designers in Visual Studio .NET that use it to be able to insert generated fragments of code into existing source files. But given how much effort it is to build the entire source file from scratch, I was impressed at how easy it was to generate a snippet.


Copyright © 2002-2013, Interact Software Ltd. Please direct all Web site inquiries to webmaster@interact-sw.co.uk