oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

Using the CodeDOM
Pages: 1, 2, 3

Implement a Loop Structure

We can use a function similar to this to build a loop structure.

private CodeIterationStatement CreateLoop(string LoopControlVariableName)
  // Declare a new variable that will be 
  // the loop control variable
  CodeVariableDeclarationStatement Declaration;

  // Declare a CodeIterationStatement which 
  // will house all of the loop logic
  CodeIterationStatement forloop = new CodeIterationStatement();
  // As an alternate method of specifying the 
  // data type for a variable
  // dynamically declared, we can use the typeof 
  // function to get the Type object
  // for a datatype without having to use a 
  // variable of that type.
  Declaration = new CodeVariableDeclarationStatement(typeof  (int),
  // Specify a very simple initialization expression 
  // of simply setting the new variable to zero.
  Declaration.InitExpression = new CodeSnippetExpression ("0");
  // Specify that this newly declared variable will 
  // be used to initialize the loop
  forloop.InitStatement = Declaration;
  // The CodeAssignStatement is used to handle 
  // assignment statements. The constructor we are 
  // using here expects two expressions, the first will
  // be on the left of the assignment.   The second 
  // will be on the right of the assigment.   Alternately, 
  // we could also call the default constructor and then
  // set the left and rigth properties explicitely.
  CodeAssignStatement assignment = new CodeAssignStatement( 
    new CodeVariableReferenceExpression(LoopControlVariableName), 
    new CodeSnippetExpression (LoopControlVariableName + " + 1" ));
  // Specify that we will use the assignment statement to iterate
  // through the loop.
  forloop.IncrementStatement = assignment;
  // Specify that the loop should end when 
  // the loop control variable exceeds the
  // number of characters in the array.
  forloop.TestExpression = new CodeSnippetExpression
    (LoopControlVariableName + " < Characters.Length");
  return forloop;

You'll note that we specified the data type information for the loop control variable using the typeof function to get the Type object directly, instead of declaring a CodeTypeReference object. This is just another constructor for the CodeVariableDeclartionStatement. There are a total of seven different constructors available.

Index an Array

We can use a function similar to this to index an array.

private CodeArrayIndexerExpression 
  CreateArrayIndex(string ArrayName, string IndexValue  )
  // Declare a new CodeArrayIndexerExpression
  CodeArrayIndexerExpression index = new CodeArrayIndexerExpression ();

  // The Indices property is a collection to support indexing into a
  // muli-dimensioanl array.   Here we are only interested in a simple
  // single dimensional array.
  index.Indices.Add ( new  CodeVariableReferenceExpression (IndexValue));
  // The TargetObject specifies the name of the array to be indexed
  index.TargetObject = new CodeSnippetExpression (ArrayName);
  return index;

The CodeArrayIndexerExpression object handles the various differences in the way that arrays are indexed. Specifically, in C#, arrays would be indexed like ArrayName[IndexValue], but in VB.Net, arrays would be indexed like ArrayName(IndexValue). This object allows us to ignore these differences and focus on details such as which array is being indexed and where in the array are we wanting to go.

Bringing it All Together (Hello World!)

All of the functions defined earlier can be added to a class and initialized in a constructor similar to this:

public CodeDomProvider()
  CurrentNameSpace = InitializeNameSpace("ComputerSpeaks");
  CodeTypeDeclaration ctd = CreateClass ("HelloWorld");
  // Add the class to the namespace
  CurrentNameSpace.Types.Add (ctd);
  CodeEntryPointMethod mtd = CreateMethod();
  // Add the method to the class
  ctd.Members.Add (mtd);
  CodeVariableDeclarationStatement VariableDeclaration = 
    DeclareVariables (typeof (StringBuilder), "sbMessage");
  // Add the variable declaration to the method 
  mtd.Statements.Add (VariableDeclaration);
  CodeVariableDeclarationStatement array = InitializeArray
    ("Characters", 'H', 'E', 'L', 'L', 'O', ' ', 
    'W', 'O', 'R', 'L', 'D');
  // Add the initialized array to the method.
  mtd.Statements.Add (array);
  CodeIterationStatement loop = CreateLoop("intCharacterIndex");
  // Add the loop to the method
  mtd.Statements.Add (loop);
  // Build an index into the initialized array.
  CodeArrayIndexerExpression index = CreateArrayIndex("Characters",
  // Add a statement that will invoke the "Append" mehtod of the
  // sbMessage object passing a parameter of the array index
  // result.
  loop.Statements.Add (new CodeMethodInvokeExpression (
    new CodeSnippetExpression ("sbMessage"),"Append", 
  // After the loop finishes, print out the result of
  // all of the appends to the sbMessage object.
  mtd.Statements.Add (new CodeSnippetExpression
    ("Console.WriteLine (sbMessage.ToString())"));

The end result of such a constructor is a fully populated CodeDom tree but no code. Everything done so far has been completely independent of the target language. The code produced will be exposed as properties.

Exposing the Generated Code

Once the CodeDom tree is populated, producing code in any .NET language is relatively straightforward. Each language includes a CodeProvider object with a CreateGenerator method that will return an object implementing the ICodeGenerator interface. This interface defines all of the methods for generating code and allows us to write a helper function that will simplify writing the properties. The properties will only have to worry about passing the appropriate CodeGenerator to our GenerateCode helper function. The GenerateCode method will handle setting up a suitable TextWriter into which the code will be produced, and returning the resulting text to the property as a string.

private string GenerateCode (ICodeGenerator CodeGenerator)
  // The CodeGeneratorOptions object allows us to specify
  // various formatting settings that will be used 
  // by the generator.
  CodeGeneratorOptions cop = new CodeGeneratorOptions();

  // Here we specify that the curley braces should start 
  // on the line following the opening of the block
  cop.BracingStyle = "C";
  // Here we specify that each block should be 
  // indented by 2 spaces
  cop.IndentString = "  ";
  // The GenerateCodeFromNamepsace method expects to be 
  // passed a TextWriter that will hold the code being 
  // produced.    This could be a StreamWriter,
  // a StringWriter, or an IndentedTextWriter.   
  // A StreamWriter can be used to output the code to 
  // a file.    A StringWriter can be bound to a StringBuilder
  // which can be referenced as a local variable.
  // Here we will bind a StringWriter to the StringBuilder sbCode.
  StringBuilder sbCode  = new StringBuilder();
  StringWriter sw  = new StringWriter(sbCode);
  // Generate the Code!
  CodeGenerator.GenerateCodeFromNamespace(CurrentNameSpace, sw,cop);
  return sbCode.ToString();

Using this helper function, the language specific properties are fairly straightforward.

public string VBCode 
    VBCodeProvider provider =  new VBCodeProvider ();
    ICodeGenerator codeGen = provider.CreateGenerator ();
    return GenerateCode (codeGen);

public string JScriptCode
    JScriptCodeProvider provider = new JScriptCodeProvider ();
    ICodeGenerator codeGen = provider.CreateGenerator ();
    return GenerateCode(codeGen);


public string JSharpCode 
    VJSharpCodeProvider provider = new VJSharpCodeProvider ();
    ICodeGenerator codeGen = provider.CreateGenerator ();
    return GenerateCode (codeGen);


public string CSharpCode 
    CSharpCodeProvider provider = new CSharpCodeProvider();
    ICodeGenerator codeGen = provider.CreateGenerator ();
    return GeneratorCode (codeGen);


Pages: 1, 2, 3

Next Pagearrow