Microsoft .NET CodeDom Technology

By Brian J. Korzeniowski


This article will introduce you to the Microsoft.NET Framework CodeDom Namespaces and allow you to take advantage of this new technology in your own applications. You will learn about the advantages of CodeDom, the disadvantages of CodeDom, the CodeDom namespaces and complete a practical programming exercise using CodeDom.


Requirements




  1. Visual Studio.NET Professional, or Enterprise Architect Edition
  2. VB.NET or C#.NET Programming Knowledge




Contents



Introduction

Tree Data Structures

Advantages of CodeDom Technology

Limitations of CodeDom Technology

System.CodeDom Namespace Overview

System.CodeDom.Compiler Namespace Overview

CodeDom Example

CodeDom Example Explanation

Conclusion

About the Author



Introduction


The introduction of the Microsoft.NET Framework, and Visual Studio.NET, represent more than an evolution in development skills and methodologies for Microsoft Developers – it represents a revolution in how software is designed, written and deployed. Today is a brave new world compared to the days of Visual Basic 4.0, and the introduction of objects, to where we are today. The tools, technologies and paradigms of the past are being advanced by the revolutions in software development created by .NET Technologies today.


One of the most promising new technologies in the .NET Framework is CodeDom, which stands for the Code Document Object Model. A popular use of the CodeDom is for developing automatic source code generators. The goal of code generators, of course, is to minimize repetitive coding tasks, and to minimize the number of human-generated source code lines being created. The CodeDom namespaces in the .NET Framework allow developers to create their own code generation utilities. In this article, we will get an overview of the CodeDom namespaces and build upon our discoveries as we examine the inputs and outputs of a working code generator. Follow-on parts to this article will cover the CodeDom namespaces in more detail.



Tree Data Structures



The tree data structure has become a common way of representing data. A tree is a collection of nodes linked together that represents data and creates a storage structure in memory. In the basic case, a tree looks like figure 1.0.



Trees consist of nodes. A node is a conceptual placeholder for a data element in a tree. See figure 1.1.




Individual nodes are linked together to form a tree structure in memory. Every tree structure has a root node. A root node is a node to which all other nodes in the tree structure are conceptually connected. See figure 1.2.




In other words, the root node is the ultimate parent of every node in the tree. When linking nodes together to form the tree structure. The node we are linking can be one of two types: a parent node or child node. A parent node is a node that contains one or more nodes linked beneath it. See figure 1.3.




A child node is a node that is linked to one parent node. See figure 1.4.




To locate data in a tree, the tree must be traversed. When a tree is traversed, an algorithm is applied to the tree which determines how the tree is searched for information. In summary, we learned that nodes are conceptual placeholders for data elements and are linked to form a structure in memory. We call this structure a tree structure. Trees are searched, or traversed, by applying a searching algorithm to the tree to retrieve data from its nodes. It is important to understand trees, because trees are the key to understanding the conceptual output of the CodeDom namespaces.


We only covered the tree data structure in a generic, high-level overview. If you are interested in further explanations, see the resources at the end of this article or read one of the many tutorials available on the subject.


Advantages of CodeDom Technology



The CodeDom namespaces in the .NET Framework provide these advantages:



  1. The CodeDom is based upon a single model for rendering source code. Therefore, source code may be generated for any language that supports the CodeDom specification.
  2. The CodeDom allows programs to be dynamically created, compiled, and executed at runtime.
  3. The CodeDom provides a language independent object model for representing the structure of source code in memory.
  4. Future releases of the CodeDom could translate source code files between languages a lot like graphic file converter programs do with graphics files today. For example, a VB.NET program could be represented by the CodeDom then translated into C# for another developer. Cool huh?


Limitations of CodeDom Technology



The CodeDom namespaces contain classes to conceptually represent most programming constructs. Examples of these include: declarations, statements, iterations, arrays, casts, comments, error handling, and others. However, there are limitations to the current implementation of the CodeDom which will remain until the CodeDom namespaces are updated by Microsoft. In the meanwhile, to represent constructs not presently supported in CodeDom you can use the “Snippet” classes. These classes allow you to insert literal lines of source code into a compile unit. The classes available for handling snippets include: the CodeSnippetCompileUnit, CodeSnippetExpression, CodeSnippetStatement, and CodeSnippetTypeMember classes. You can use the snippet classes as a generic “catch all” for implementing presently unsupported programming constructs in the CodeDom. As the CodeDom namespaces are enhanced by Microsoft, you should not need to rely as heavily upon the snippet classes. As with any new technology, there are limitations to its functionality – CodeDom technology is no exception. We will look at four useful programming constructs not presently supported by the CodeDom. There are many more examples of missing constructs unsupported by the current implementation of CodeDom, but these should give you an idea of the enhancements forthcoming in the CodeDom namespaces.



  1. Limitation #1: There is no support for aliasing a namespace.


    namespace MyNamespace
    {
    public void MyClass
    {
    }
    }
    namespace MyNamespace2
    {
    using MyAlias = MyNamespace.MyClass;
    public void MyClass2 : MyAlias
    {
    }
    }



  2. Limitation #2: There is no support for nested namespaces.


    namespace OutterNamespace
    {
    namespace InnerNamespace
    {
    public void MyClass
    {
    }
    }
    }

  3. Limitation #3: There is no support for variable declaration lists.


    using System;
    using System.CodeDom;
    using System.CodeDom.Compiler;

    namespace MyNamespace
    {
    public void MyClass
    {
    public void MyClass()
    {
    int i,j,k;
    bool blnYes, blnNo;
    }
    }
    }


  4. Limitation #4: There is no support for the “unsafe” modifier in C#.


    using System;

    class MyUnsafeClass
    {
    unsafe static void MyPointerFn(int* ptr)

    {
    *p *= *p;
    {
    {
    unsafe public static void main()
    {
    //unsafe operation using address-of operator &
    int i = 10;
    MyPointerFn(&i);
    Console.WriteLine(i);
    }



    System.CodeDom Namespace Overview


    The System.CodeDom Namespace contains classes, interfaces and enumerations to represent source code trees in memory. Each source code tree is called a CompileUnit. CompileUnits are linked together to form a tree structure in memory representing the structure of the source code. Remember our discussion on trees? Each CodeDom class is analogous to a node in the source tree. Each CodeDom node contains data which is used by the ICodeGenerator Interface later on. The ICodeGenerator Interface is what receives the Compile Units and outputs source code in the designated language. Compile Units are the key to understanding how the CodeDom creates the internal representation of the source code in memory.

     

    System.CodeDom Enumerations

    CodeBinaryOperatorType

    Specifies
    identifiers for supported binary operators.

    FieldDirection

    Specifies
    identifiers used to indicate the direction of parameter and argument
    declarations.

    MemberAttributes

    Specifies
    member attribute identifiers for class members.

     

     

    System.CodeDom Classes

    CodeArgumentReferenceExpression

    Represents
    a reference to an argument.

    CodeArrayCreateExpression

    Represents
    an expression that creates an array.

    CodeArrayIndexerExpression

    Represents an expression
    that indicates an array and a specific index or indices.

    CodeAssignStatement

    Represents
    a simple assignment statement.

    CodeAttachEventStatement

    Represents a statement
    that attaches an event handler.

    CodeAttributeArgument

    Represents
    an argument used in a metadata custom attribute declaration.

    CodeAttributeArgumentCollection

    Represents
    a collection of CodeAttributeArgument
    objects.

    CodeAttributeDeclaration

    Represents
    an attribute declaration.

    CodeAttributeDeclarationCollection

    Represents
    a collection of CodeAttributeDeclaration
    objects.

    CodeBaseReferenceExpression

    Represents
    a reference to the base class.

    CodeBinaryOperatorExpression

    Represents
    an expression that consists of a binary operation between two expressions.

    CodeCastExpression

    Represents
    an expression that is to be cast to a data type or interface.

    CodeCatchClause

    Represents
    a catch exception block.

    CodeCatchClauseCollection

    Represents
    a collection of CodeCatchClause
    objects.

    CodeComment

    Represents
    a comment.

    CodeCommentStatement

    Represents
    a statement consisting of a single comment.

    CodeCommentStatementCollection

    Represents a collection
    of CodeCommentStatement
    objects.

    CodeCompileUnit

    Provides a top-level
    object to use for compilation.

    CodeConditionStatement

    Represents
    a conditional branch statement, typically represented as an if statement.

    CodeConstructor

    Represents
    the declaration of an instance constructor for a type.

    CodeDelegateCreateExpression

    Represents
    an expression that creates a delegate.

    CodeDelegateInvokeExpression

    Represents an expression
    that invokes a delegate.

    CodeDirectionExpression

    Represents an expression
    that indicates the direction type of the reference.

    CodeEntryPointMethod

    Represents
    the entry point of an executable.

    CodeEventReferenceExpression

    Represents an expression
    that references an event.

    CodeExpression

    Represents a code
    expression. This is a base class for other code expression objects that is
    never instantiated.

    CodeExpressionCollection

    Represents a collection
    of CodeExpression
    objects.

    CodeExpressionStatement

    Represents
    a statement that consists of a single expression.

    CodeFieldReferenceExpression

    Represents a reference to
    a field.

    CodeGotoStatement

    Represents a goto
    statement.

    CodeIndexerExpression

    Represents a reference to
    an indexer property of an object.

    CodeIterationStatement

    Represents a for
    statement, or a simple loop through a block of statements, using a test
    expression as a condition for continuing to loop.

    CodeLabeledStatement

    Represents a labeled
    statement or a stand-alone label.

    CodeLinePragma

    Represents a specific
    location within a specific file.

    CodeMemberEvent

    Represents an event
    member of a class.

    CodeMemberField

    Represents a field class
    member declaration.

    CodeMemberMethod

    Represents a declaration
    for a method of a class.

    CodeMemberProperty

    Represents
    a declaration for a property of a class.

    CodeMethodInvokeExpression

    Represents
    an expression that invokes a method.

    CodeMethodReferenceExpression

    Represents
    an expression that references a method on a specific object.

    CodeMethodReturnStatement

    Represents a return
    statement.

    CodeNamespace

    Represents a namespace
    declaration.

    CodeNamespaceCollection

    Represents a collection
    of CodeNamespace
    objects.

    CodeNamespaceImport

    Represents
    a namespace import directive that indicates a namespace to use.

    CodeNamesapceImportCollection

    Represents a collection
    of CodeNamespaceImport
    objects.

    CodeObject

    Provides a common base
    class for most Code Document Object Model (CodeDOM) objects.

    CodeObjectCreateExpression

    Represents an expression
    that creates a new instance of an object.

    CodeParamaterDeclarationExpression

    Represents a parameter
    declaration for a method, property, or constructor.

    CodeParameterDeclarationExpressionCollection

    Represents a collection
    of CodeParameterDeclarationExpression
    objects.

    CodePrimitiveExpression

    Represents a primitive
    data type value.

    CodePropertyReferenceExpression

    Represents
    a reference to a property.

    CodePropertySetValueReferenceExpression

    Represents an expression
    that represents the value argument of a property set method call within a
    property set method declaration.

    CodeRemoveEventStatement

    Represents
    a statement that removes an event handler.

    CodeSnippetCompileUnit

    Represents a literal code
    fragment that can be compiled.

    CodeSnippetExpression

    Represents a literal
    expression.

    CodeSnippetStatement

    Represents a statement
    using a literal code fragment.

    CodeSnippetTypeMember

    Represents a member of a
    class using a literal code fragment.

    CodeStatementCollection

    Represents a collection
    ofCodeStatement
    objects.

    CodeThisReferenceExpression

    Represents a reference to
    the current local class instance.

    CodeThrowExceptionStatement

    Represents
    a statement that throws an exception.

    CodeTryCatchFinallyStatement

    Represents a try block,
    with any number of catch clauses and optionally, a finally block.

    CodeTypeConstructor

    Represents a static
    constructor for a class.

    CodeTypeDeclaration

    Represents a type
    declaration for a class, structure, interface or enumeration.

    CodeTypeDeclarationCollection

    Represents a collection
    of CodeTypeDeclaration
    objects.

    CodeTypeDelegate

    Represents a delegate
    declaration.

    CodeTypeMember

    Represents the
    declaration for a member of a type. Type members include fields, methods,
    properties, constructors and nested types.

    CodeTypeMemberCollection

    Represents a collection
    of CodeTypeMember
    objects.

    CodeTypeOfExpression

    Represents a typeof
    expression, an expression that returns a specified runtime type.

    CodeTypeReference

    Represents a data type to
    CodeDOM objects.

    CodeTypeReferenceCollection

    Represents a collection
    of CodeTypeReference
    objects.

    CodeTypeReferenceExpression

    Represents a reference to
    a data type.

    CodeVariableDeclarationStatement

    Represents a declaration
    of a variable.

    CodeVariableReferenceExpression

    Represents an expression
    that references a local variable.



    System.CodeDom.Compiler Namespace Overview



    The System.CodeDom.Compiler namespace contains enumerations, interfaces and classes used to generate and compile source code. Compile Units created using the System.CodeDom namespace are collected and processed by the System.CodeDom.Compiler namespace. When generating source code, CompileUnits are processed by three main interfaces: the ICodeParser, ICodeGenerator, and ICodeCompiler Interfaces. The ICodeParser Interface parses the CompileUnit trees into a structure in memory. The ICodeGenerator Interface reads the output of the ICodeParser Interface and physically generates the source code files in the desired language. The ICodeCompiler Interface receives source code files as input and compiles the source code into assemblies. The key to understanding the System.CodeDom.Compiler namespace is to learn how the three exposed interfaces work: the ICodeParser, ICodeCompiler, and ICodeCompiler Interfaces. In short, the System.CodeDom.Compiler namespace operates on the premise of having tree structures called Compile Units already built and ready to be parsed, generated and compiled.

     

    System.CodeDom.Compiler Enumerations

    GeneratorSupport

    Specifies
    identifiers used to determine whether a code generator supports certain types
    of code.

    LanguageOptions

    Specifies
    identifiers that indicate special features of a language.

     

     

    System.CodeDom.Compiler Interfaces

    ICodeCompiler

    Provides
    a compiler execution interface.

    ICodeGenerator

    Provides
    an interface for generating code.

    ICodeParser

    Specifies an interface
    for parsing code into a CodeCompileUnit.

     

     

    System.CodeDom.Compiler
    Classes

    CodeCompiler

    Provides
    a helper class for implementing an ICodeCompiler.

    CodeDomProvider

    Provides
    a base class for CodeDomProvider implementations. This class is
    abstract.

    CodeGenerator

    Provides
    a helper class for implementing an ICodeGenerator.
    This class is abstract.

    CodeGeneratorOptions

    Represents
    options used by code generators.

    CodeParser

    Provides
    a helper class for implementing an ICodeParser.

    CompilerError

    Represents
    a compiler error or warning.

    CompilerErrorCollection

    Represents
    a collection of CompilerError
    objects.

    CompilerParameters

    Represents
    the parameters used to invoke the compiler.

    CompilerResults

    Represents
    the results of compilation that are returned from the compiler.

    Executor

    Provides
    command execution functions for invoking compilers. This class cannot be
    inherited.

    IndentedTextWriter

    Provides
    a text writer that can indent new lines by a tab string token.

    TempFileCollection

    Represents a collection
    of temporary files.



    CodeDom Example



    Figure 1.5: Source Code Generator



    1: using System;
    2: using System.CodeDom;
    3: using System.CodeDom.Compiler;
    4: using System.Reflection;
    5: using System.IO;
    6: using Microsoft.CSharp;
    7: using Microsoft.VisualBasic;
    8:
    9 : namespace CodeDomPartOne
    10: {
    11: ///

    12: /// Summary description for Briefcase.
    13: ///

    14: public class Briefcase
    15: {
    16: //Member Variables
    17: private string m_strFileName;
    18: private string m_Suffix = “.cs”;
    19:
    20: public Briefcase(string strFileName)
    21: {
    22: m_strFileName = strFileName;
    23: }
    24:
    25: public void CreateCodeDomBriefcase()
    26: {
    27: //Initialize CodeDom Variables
    28: Stream s = File.Open(“c:\\” + m_strFileName + m_Suffix, FileMode.Create);
    29: StreamWriter sw = new StreamWriter(s);
    30:
    31: CSharpCodeProvider cscProvider = new CSharpCodeProvider();
    32: ICodeGenerator cscg = cscProvider.CreateGenerator(sw);
    33: CodeGeneratorOptions cop = new CodeGeneratorOptions();
    34:
    35: //Create Class Using Statements
    36: CodeSnippetCompileUnit csu1 = new CodeSnippetCompileUnit(“using System”);
    37: CodeSnippetCompileUnit csu2 = new CodeSnippetCompileUnit(“using System.IO”);
    38: cscg.GenerateCodeFromCompileUnit(csu1, sw, cop);
    39: cscg.GenerateCodeFromCompileUnit(csu2, sw, cop);
    40: sw.WriteLine();
    41:
    42: //Create Class Namespaces
    43: CodeNamespace cnsCodeDom = new CodeNamespace(“CodeDom”);
    44:
    45: //Create Class Declaration
    46: CodeTypeDeclaration ctd = new CodeTypeDeclaration();
    47: ctd.IsClass = true;
    48: ctd.Name = “Briefcase”;
    49: ctd.TypeAttributes = TypeAttributes.Public;
    50:
    51: //Create Class Member Fields
    52: sw.WriteLine();
    53: CodeMemberField cmfBriefcaseName = new CodeMemberField(“string”,”m_BriefcaseName”);
    54: cmfBriefcaseName.Attributes = MemberAttributes.Private;
    55: ctd.Members.Add(cmfBriefcaseName);
    56:
    57: CodeMemberField cmfBriefcaseTitle = new CodeMemberField(“string”, “m_BriefcaseTitle”);
    58: cmfBriefcaseTitle.Attributes = MemberAttributes.Private;
    59: ctd.Members.Add(cmfBriefcaseTitle);
    60:
    61: CodeMemberField cmfBriefcaseID = new CodeMemberField(“int”, “m_cmfBriefcaseID”);
    62: cmfBriefcaseID.Attributes = MemberAttributes.Private;
    63: ctd.Members.Add(cmfBriefcaseID);
    64:
    65: CodeMemberField cmfBriefcaseSectionID = new CodeMemberField(“int”, “m_BriefcaseSectionID”);
    66: cmfBriefcaseSectionID.Attributes = MemberAttributes.Private;
    67: ctd.Members.Add(cmfBriefcaseSectionID);
    68:
    69: CodeMemberField cmfBriefcaseFolderID = new CodeMemberField(“int”, “m_BriefcaseFolderID”);
    70: cmfBriefcaseFolderID.Attributes = MemberAttributes.Private;
    71: ctd.Members.Add(cmfBriefcaseFolderID);
    72:
    73: CodeMemberField cmfBriefcaseItemID = new CodeMemberField(“int”, “m_BriefcaseItemID”);
    74: cmfBriefcaseItemID.Attributes = MemberAttributes.Private;
    75: ctd.Members.Add(cmfBriefcaseItemID);
    76:
    77: //Create Class Constructor
    78: CodeConstructor ccon = new CodeConstructor();
    79: ccon.Attributes = MemberAttributes.Public;
    80: ccon.Statements.Add(new CodeSnippetStatement(“//”));
    81: ccon.Statements.Add(new CodeSnippetStatement(“// TODO: Add constructor logic here”));
    82: ccon.Statements.Add(new CodeSnippetStatement(“//”));
    83: ctd.Members.Add(ccon);
    84:
    85: //Create Class BriefcaseName Property
    86: CodeMemberProperty mpBriefcaseName = new CodeMemberProperty();
    87: mpBriefcaseName.Attributes = MemberAttributes.Private;
    88: mpBriefcaseName.Type = new CodeTypeReference(“string”);
    89: mpBriefcaseName.Name = “BriefcaseName”;
    90: mpBriefcaseName.HasGet = true;
    91: mpBriefcaseName.GetStatements.Add(new CodeSnippetExpression(“return m_BriefcaseName”));
    92: mpBriefcaseName.HasSet = true;
    93: mpBriefcaseName.SetStatements.Add(new CodeSnippetExpression(“m_BriefcaseName = value”));
    94: ctd.Members.Add(mpBriefcaseName);
    95:
    96: //Create Class BriefcaseTitle Property
    97: CodeMemberProperty mpBriefcaseTitle = new CodeMemberProperty();
    98: mpBriefcaseTitle.Attributes = MemberAttributes.Private;
    99: mpBriefcaseTitle.Type = new CodeTypeReference(“string”);
    100: mpBriefcaseTitle.Name = “BriefcaseTitle”;
    101: mpBriefcaseTitle.HasGet = true;
    102: mpBriefcaseTitle.GetStatements.Add(new CodeSnippetExpression(“return m_BriefcaseTitle”));
    103: mpBriefcaseTitle.HasSet = true;
    104: mpBriefcaseTitle.SetStatements.Add(new CodeSnippetExpression(“m_BriefcaseTitle = value”));
    105: ctd.Members.Add(mpBriefcaseTitle);
    106:
    107: //Create Class BriefcaseID Property
    108: CodeMemberProperty mpBriefcaseID = new CodeMemberProperty();
    109: mpBriefcaseID.Attributes = MemberAttributes.Private;
    110: mpBriefcaseID.Type = new CodeTypeReference(“int”);
    111: mpBriefcaseID.Name = “BriefcaseID”;
    112: mpBriefcaseID.HasGet = true;
    113: mpBriefcaseID.GetStatements.Add(new CodeSnippetExpression(“m_BriefcaseID”));
    114: mpBriefcaseID.HasSet = true;
    115: mpBriefcaseID.SetStatements.Add(new CodeSnippetExpression(“m_BriefcaseID = value”));
    116: ctd.Members.Add(mpBriefcaseID);
    117:
    118: //Create Class BriefcaseSection Property
    119: CodeMemberProperty mpBriefcaseSection = new CodeMemberProperty();
    120: mpBriefcaseSection.Attributes = MemberAttributes.Private;
    121: mpBriefcaseSection.Type = new CodeTypeReference(“int”);
    122: mpBriefcaseSection.Name = “BriefcaseSection”;
    123: mpBriefcaseSection.HasGet = true;
    124: mpBriefcaseSection.GetStatements.Add(new CodeSnippetExpression
    125: (“return m_BriefcaseSectionID”));
    126: mpBriefcaseSection.HasSet = true;
    127: mpBriefcaseSection.SetStatements.Add(new CodeSnippetExpression
    128: (“m_BriefcaseSectionID = value”));
    129: ctd.Members.Add(mpBriefcaseSection);
    130:
    131: //Create Class BriefcaseFolder Property
    132: CodeMemberProperty mpBriefcaseFolder = new CodeMemberProperty();
    133: mpBriefcaseFolder.Attributes = MemberAttributes.Private;
    134: mpBriefcaseFolder.Type = new CodeTypeReference(“int”);
    135: mpBriefcaseFolder.Name = “BriefcaseFolder”;
    136: mpBriefcaseFolder.HasGet = true;
    137: mpBriefcaseFolder.GetStatements.Add(new CodeSnippetExpression(“return m_BriefcaseFlderID”));
    138: mpBriefcaseFolder.HasSet = true;
    139: mpBriefcaseFolder.SetStatements.Add(new CodeSnippetExpression(“m_BriefcaseFolderID = value”));
    140: ctd.Members.Add(mpBriefcaseFolder);
    141:
    142: //Create Class BriefcaseItem Property
    143: CodeMemberProperty mpBriefcaseItem = new CodeMemberProperty();
    144: mpBriefcaseItem.Attributes = MemberAttributes.Private;
    145: mpBriefcaseItem.Type = new CodeTypeReference(“string”);
    146: mpBriefcaseItem.Name = “BriefcaseItem”;
    147: mpBriefcaseItem.HasGet = true;
    148: mpBriefcaseItem.GetStatements.Add(new CodeSnippetExpression(“return m_BriefcaseItemID”));
    149: mpBriefcaseItem.HasSet = true;
    150: mpBriefcaseItem.SetStatements.Add(new CodeSnippetExpression(“m_BriefcaseItemID = value”));
    151: ctd.Members.Add(mpBriefcaseItem);
    152:
    153: //Create Class GetBriefcaseName Method
    154: CodeMemberMethod mtd1 = new CodeMemberMethod();
    155: mtd1.Name = “GetBriefcaseName”;
    156: mtd1.ReturnType = new CodeTypeReference(“String”);
    157: mtd1.Attributes = MemberAttributes.Public;
    158: mtd1.Statements.Add(new CodeSnippetStatement(“return BriefcaseName;”));
    159: ctd.Members.Add(mtd1);
    160:
    161: //Create Class GetBriefcaseTitle Method
    162: CodeMemberMethod mtd2 = new CodeMemberMethod();
    163: mtd2.Name = “GetBriefcaseTitle”;
    164: mtd2.ReturnType = new CodeTypeReference(“String”);
    165: mtd2.Attributes = MemberAttributes.Public;
    166: mtd2.Statements.Add(new CodeSnippetStatement(“return BriefcaseTitle;”));
    167: ctd.Members.Add(mtd2);
    168:
    169: //Create Class GetBriefcaseID Method
    170: CodeMemberMethod mtd3 = new CodeMemberMethod();
    171: mtd3.Name = “GetBriefcaseID”;
    172: mtd3.ReturnType = new CodeTypeReference(“Int”);
    173: mtd3.Attributes = MemberAttributes.Public;
    174: mtd3.Statements.Add(new CodeSnippetStatement(“return BriefcaseID;”));
    175: ctd.Members.Add(mtd3);
    176:
    177: //Create Class GetBriefcaseSection Method
    178: CodeMemberMethod mtd4 = new CodeMemberMethod();
    179: mtd4.Name = “GetBriefcaseSectionID”;
    180: mtd4.ReturnType = new CodeTypeReference(“Int”);
    181: mtd4.Attributes = MemberAttributes.Public;
    182: mtd4.Statements.Add(new CodeSnippetStatement(“return BriefcaseSectionID;”));
    183: ctd.Members.Add(mtd4);
    184:
    185: //Create Class GetBriefcaseFolder Method
    186: CodeMemberMethod mtd5 = new CodeMemberMethod();
    187: mtd5.Name = “GetBriefcaseFolderID”;
    188: mtd5.ReturnType = new CodeTypeReference(“Int”);
    189: mtd5.Attributes = MemberAttributes.Public;
    190: mtd5.Statements.Add(new CodeSnippetStatement(“return BriefcaseFolderID;”));
    191: ctd.Members.Add(mtd5);
    192:
    193: //Create Class GetBriefcaseItem Method
    194: CodeMemberMethod mtd6 = new CodeMemberMethod();
    195: mtd6.Name = “GetBriefcaseItemID”;
    196: mtd6.ReturnType = new CodeTypeReference(“Int”);
    197: mtd6.Attributes = MemberAttributes.Public;
    198: mtd6.Statements.Add(new CodeSnippetStatement(“return BriefcaseItemID;”));
    199: ctd.Members.Add(mtd6);
    200:
    201: //Generate Source Code File
    202: cscg.GenerateCodeFromNamespace(cnsCodeDom, sw, cop);
    203:
    204: //Close StreamWriter
    205: sw.Close();
    206: s.Close();
    207: }
    208: }
    209: }


    Figure 1.6: Source Code Generated



    1: using System
    2: using System.IO
    3:
    4: namespace CodeDom {
    5:
    6: public class Briefcase {
    7:
    8: private string m_BriefcaseName;
    9: private string m_BriefcaseTitle;
    10: private int m_cmfBriefcaseID;
    11: private int m_BriefcaseSectionID;
    12: private int m_BriefcaseFolderID;
    13: private int m_BriefcaseItemID;
    14:
    15: public Briefcase() {
    16: //
    17: // TODO: Add constructor logic here
    18: //
    19: }
    20:
    21: private string BriefcaseName {
    22: get {
    23: return m_BriefcaseName;
    24: }
    25: set {
    26: m_BriefcaseName = value;
    27: }
    28: }
    29:
    30: private string BriefcaseTitle {
    31: get {
    32: return m_BriefcaseTitle;
    33: }
    34: set {
    35: m_BriefcaseTitle = value;
    36: }
    37: }
    38:
    39: private int BriefcaseID {
    40: get {
    41: m_BriefcaseID;
    42: }
    43: set {
    44: m_BriefcaseID = value;
    45: }
    46: }
    47:
    48: private int BriefcaseSection {
    49: get {
    50: return m_BriefcaseSectionID;
    51: }
    52: set {
    53: m_BriefcaseSectionID = value;
    54: }
    55: }
    56:
    57: private int BriefcaseFolder {
    58: get {
    59: return m_BriefcaseFlderID;
    60: }
    61: set {
    62: m_BriefcaseFolderID = value;
    63: }
    64: }
    65:
    66: private string BriefcaseItem {
    67: get {
    68: return m_BriefcaseItemID;
    69: }
    70: set {
    71: m_BriefcaseItemID = value;
    72: }
    73: }
    74:
    75: public virtual String GetBriefcaseName() {
    76: return BriefcaseName;
    77: }
    78:
    79: public virtual String GetBriefcaseTitle() {
    80: return BriefcaseTitle;
    81: }
    82:
    83: public virtual Int GetBriefcaseID() {
    84: return BriefcaseID;
    85: }
    86:
    87: public virtual Int GetBriefcaseSectionID() {
    88: return BriefcaseSectionID;
    89: }
    90:
    91: public virtual Int GetBriefcaseFolderID() {
    92: return BriefcaseFolderID;
    93: }
    94:
    95: public virtual Int GetBriefcaseItemID() {
    96: return BriefcaseItemID;
    97: }
    98 }
    99: }



    CodeDom Example Explanation


    Figure 1.5 contains the CodeDom instructions to generate a C# class file named “Briefcase.” We will now examine this code to gain a better understanding of the principles involved in CodeDom Source Code Construction.



    1: using System;
    2: using System.CodeDom;
    3: using System.CodeDom.Compiler;
    4: using System.Reflection;
    5: using System.IO;
    6: using Microsoft.CSharp;
    7: using Microsoft.VisualBasic;

    This code declares the namespaces, which you use to reference most of the .NET Framework classes that are of interest in your application.



    9 : namespace CodeDomPartOne

    This line declares a namespace that other developers will use to reference your new, custom CodeDom class. For example, another developer would include this statement in their program to declare their own variable based upon your class: CodeDomPartOne.Briefcase myBriefcase.


    11: ///

    12: /// Summary description for Briefcase.
    13: ///


    This code block declares inline comments, which describe the purpose of your function. The format shown will work only for C# code. There are other elements you can include in your own commenting as well. To see what these are, simply type three slashes as shown above, and scroll through the listing of available commenting options.



    14: public class Briefcase
    15: {

    This code declares the class that contains the methods used to generate your Briefcase Object “on-the-fly.” Again, to instantiate this class, developers will use this syntax: CodeDomPartOne.Briefcase myBriefcase.


    16: //Member Variables
    17: private string m_strFileName;
    18: private string m_Suffix = “.cs”;

    This code declares private member variables for your Briefcase object. Notice the m_Suffix variable has been assigned a member initializer value. By default, the example we are examining here will generate C# source code, hence the “.cs” file extension. Also, notice that the m_strFileName variable has not been assigned a value using a member initializer. This is because the m_strFileName variable will be initialized from within the Briefcase Class Constructor – we will look at this next.



    20: public Briefcase(string strFileName)
    21: {
    22: m_strFileName = strFileName;
    23: }

    This code declares the constructor for the Briefcase Class. Notice that it receives a file name as a string, which it then uses to initialize the m_strFileName member variable directly. Since we initialize the m_strFileName variable from within the constructor, we do not use a member initializer value to set its contents as we did with the m_Suffix member variable.



    25: public void CreateCodeDomBriefcase()
    26: {
    27: //Initialize CodeDom Variables
    28: Stream s = File.Open(“c:\\” + m_strFileName + m_Suffix, FileMode.Create);
    29: StreamWriter sw = new StreamWriter(s);

    This code creates a method named CreateCodeDomBriefcase. This method is what other developers will call to generate their own Briefcase Object “on-the-fly.” Notice a few things. First, when creating an instance of the Stream Class the output is directed in our example to the root drive (the “c:\” drive). If you wanted to create the final source code file in another location, you could simply modify the source code here to do that. Second, notice the use of the StreamWriter class. The StreamWriter class is a fast, efficient way to create files on disk. Once we have created a Stream to manage the bits and bytes that will be created by our CodeDom Classes, we need to create a mechanism for writing the bits and bytes to a physical disk medium. To accomplish this, we create a StreamWriter class and instruct the StreamWriter class to use the Stream class we created, named “s”, to write the physical bits to the disk.



    31: CSharpCodeProvider cscProvider = new CSharpCodeProvider();
    32: ICodeGenerator cscg = cscProvider.CreateGenerator(sw);
    33: CodeGeneratorOptions cop = new CodeGeneratorOptions();

    In line 31, the code creates an instance of the CSharpCodeProvider Class. If you want to use the CodeDom classes in your own projects, you must point the CodeDom Namespaces to a class that conforms to the CodeDom Specification, and that provides the functionality to generate source code in the language you select. In our case, Line 31 above tells us that the CSharpCodeProvider Class contains the necessary functionality to dynamically create source code in C# using CodeDom.


    So the CSharpCodeProvider Class contains the necessary plumbing to support code generation targeting the C# programming language. But what does Line 32 do? Once we know which class contains the functionality to perform the code generation, we must declare a class to physically manipulate the bits and write them to disk. This is what the ICodeGenerator Interface does. Later on you will see this statement: cscg.GenerateCodeFromCOmpileUnit(). GenerateCodeFomCompileUnit is a method of the ICodeGenerator Interface that will physically write the bits of source code to disk using the specified object as its source tree. In this example, the object would be a CodeCompileUnit Class.


    In Line 33, we create an object of type CodeGeneratorOptions. The CodeGeneratorOptions Class will set configuration properties for structuring the physical source code file on disk. Common properties that are set using the CodeGeneratorOptions Class include how many characters to indent, the indent character and what style of bracing to use. In our case, since we did not specify any configuration properties, and since we are targeting C# as our default language, the defaults for the CodeGeneratorOptions Class were used – “C” Style bracing, and tab character indentations.



    35: //Create Class Using Statements
    36: CodeSnippetCompileUnit csu1 = new CodeSnippetCompileUnit(“using System”);
    37: CodeSnippetCompileUnit csu2 = new CodeSnippetCompileUnit(“using System.IO”);
    38: cscg.GenerateCodeFromCompileUnit(csu1, sw, cop);
    39: cscg.GenerateCodeFromCompileUnit(csu2, sw, cop);
    40: sw.WriteLine();


    This code block has some interesting CodeDom objects. Remember us saying that the “Snippet” classes were a “catch-all” for generating source code? Here, we have used two instances of the CodeSnippetCompileUnit Class to generate our namespace declarations. Once a conceptual placeholder for the “using” namespaces has been created, the next step is to translate them into C# source code. The statements on lines 38 and 39 accomplish the task of using our code generator to generate source code for the namespaces using our CodeSnippetCompileUnit definitions. Line 40 simply inserts a blank line into the source code file.



    42: //Create Class Namespaces
    43: CodeNamespace cnsCodeDom = new CodeNamespace(“CodeDom”);

    This code simply creates a definition for a namespace that will wrap our Briefcase Class definition. When other developers declare objects based upon our class, they will use the following syntax to create them: CodeDom.Briefcase myNewBriefcaseObject. Namespaces help our code to be more easily understood by other developers by grouping classes with similar functionality together under one logical grouping name.



    45: //Create Class Declaration
    46: CodeTypeDeclaration ctd = new CodeTypeDeclaration();
    47: ctd.IsClass = true;
    48: ctd.Name = “Briefcase”;
    49: ctd.TypeAttributes = TypeAttributes.Public;

    This code block creates the definition of our Briefcase Class. It is this class that will contain a method with the CodeDom statements necessary to generate a new Briefcase Object “on-the-fly.” Since classes are types of programming constructs, we use a CodeTypeDeclaration Class to define our new Briefcase Class. After we construct the logical placeholder for a Class construct we need to configure it. This is what Lines 47-49 accomplish. Line 47 indicates that this CodeTypeDeclaration class is of type “class”. In other words, we are going to instruct the CodeDom Generator to generate a class definition when the code is generated. Line 48 simply gives our new class a name – in this case, our new class generated will be called “Briefcase”. Line 49 configures the Briefcase class to have a public scope. Therefore, this class will be visible to all other classes and modules.



    51: //Create Class Member Fields
    52: sw.WriteLine();
    53: CodeMemberField cmfBriefcaseName = new CodeMemberField(“string”,”m_BriefcaseName”);
    54: cmfBriefcaseName.Attributes = MemberAttributes.Private;
    55: ctd.Members.Add(cmfBriefcaseName);

    This code will create and configure a new Member Field for the Briefcase Class called m_BriefcaseName. Line 52 inserts a blank line into the output source code. Line 53 uses the CodeDom Class CodeMemberField to instantiate a placeholder in the CodeDom Source Code Tree for a Member Field called “m_BriefcaseName” having a data type of “string”. Line 54 sets the scope of the m_BriefcaseName member field to private. Line 55 adds the new Member Field to the class defined in Line 46 above.



    57: CodeMemberField cmfBriefcaseTitle = new CodeMemberField(“string”, “m_BriefcaseTitle”);
    58: cmfBriefcaseTitle.Attributes = MemberAttributes.Private;
    59: ctd.Members.Add(cmfBriefcaseTitle);

    This code will create and configure a new Member Field for the Briefcase Class called m_BriefcaseTitle. Line 57 uses the CodeDom Class CodeMemberField to instantiate a placeholder in the CodeDom Source Code Tree for a Member Field called “m_BriefcaseTitle” having a data type of “string”. Line 58 sets the scope of the m_BriefcaseName member field to private. Line 59 adds the new Member Field to the class defined in Line 46 above.


    61: CodeMemberField cmfBriefcaseID = new CodeMemberField(“int”, “m_cmfBriefcaseID”);
    62: cmfBriefcaseID.Attributes = MemberAttributes.Private;
    63: ctd.Members.Add(cmfBriefcaseID);

    This code will create and configure a new Member Field for the Briefcase Class called m_BriefcaseID. Line 61 uses the CodeDom Class CodeMemberField to instantiate a placeholder in the CodeDom Source Code Tree for a Member Field called “m_BriefcaseID” having a data type of “int”. Line 62 sets the scope of the m_BriefcaseID member field to private. Line 63 adds the new Member Field to the class defined in Line 46 above.



    65: CodeMemberField cmfBriefcaseSectionID = new CodeMemberField(“int”, “m_BriefcaseSectionID”);
    66: cmfBriefcaseSectionID.Attributes = MemberAttributes.Private;
    67: ctd.Members.Add(cmfBriefcaseSectionID);

    This code will create and configure a new Member Field for the Briefcase Class called m_BriefcaseSectionID. Line 65 uses the CodeDom Class CodeMemberField to instantiate a placeholder in the CodeDom Source Code Tree for a Member Field called “m_BriefcaseSectionID” having a data type of “int”. Line 66 sets the scope of the m_BriefcaseSectionID member field to private. Line 67 adds the new Member Field to the class defined in Line 46 above.



    69: CodeMemberField cmfBriefcaseFolderID = new CodeMemberField(“int”, “m_BriefcaseFolderID”);
    70: cmfBriefcaseFolderID.Attributes = MemberAttributes.Private;
    71: ctd.Members.Add(cmfBriefcaseFolderID);

    This code will create and configure a new Member Field for the Briefcase Class called m_BriefcaseFolderID. Line 69 uses the CodeDom Class CodeMemberField to instantiate a placeholder in the CodeDom Source Code Tree for a Member Field called “m_BriefcaseFolderID” having a data type of “int”. Line 70 sets the scope of the m_BriefcaseFolderID member field to private. Line 71 adds the new Member Field to the class defined in Line 46 above.



    73: CodeMemberField cmfBriefcaseItemID = new CodeMemberField(“int”, “m_BriefcaseItemID”);
    74: cmfBriefcaseItemID.Attributes = MemberAttributes.Private;
    75: ctd.Members.Add(cmfBriefcaseItemID);


    This code will create and configure a new Member Field for the Briefcase Class called m_BriefcaseItemID. Line 73 uses the CodeDom Class CodeMemberField to instantiate a placeholder in the CodeDom Source Code Tree for a Member Field called “m_BriefcaseItemID” having a data type of “int”. Line 74 sets the scope of the m_BriefcaseItemID member field to private. Line 75 adds the new Member Field to the class defined in Line 46 above.



    77: //Create Class Constructor
    78: CodeConstructor ccon = new CodeConstructor();
    79: ccon.Attributes = MemberAttributes.Public;
    80: ccon.Statements.Add(new CodeSnippetStatement(“//”));
    81: ccon.Statements.Add(new CodeSnippetStatement(“// TODO: Add constructor logic here”));
    82: ccon.Statements.Add(new CodeSnippetStatement(“//”));
    83: ctd.Members.Add(ccon);


    This code will create the constructor for the Briefcase Class. Line 78 declares a conceptual placeholder for a CodeConstructor object which will contain the instructions necessary to generate our constructor. Line 79 configures the constructor by setting its scope to public. Next, we will generate some instructions for other developers inside our constructor. We accomplish this by using the CodeSnippetStatement Class. Remember our conversation on the “Snippet” Classes? The Snippet Classes are used when the programming construct we want to implement using CodeDom is not yet supported, or we want to accomplish the same thing using a different approach. Lines 80 to 82 each declare and then add a new comment to be included inside the constructor. Notice the use of the CodeSnippetStatement Class. By using this class, we are instructing the CodeDom to replicate what is contained within the CodeSnippetStatement Class as literal text. We add three comments to the constructor for the Briefcase class. We are almost finished creating the constructor, except we have not yet attached the constructor to a class. Line 83 attaches the constructor to our Briefcase class. Because we attached the constructor to the Briefcase Class, when the source code is ultimately generated the code generator will use the same name as the class for its constructor as well. In our case, Line 83 will conceptually create the following line when generated: “public Briefcase() {}”. Next, we need to add the property definitions to our class. This is what the next code block does.



    85: //Create Class BriefcaseName Property
    86: CodeMemberProperty mpBriefcaseName = new CodeMemberProperty();
    87: mpBriefcaseName.Attributes = MemberAttributes.Private;
    88: mpBriefcaseName.Type = new CodeTypeReference(“string”);
    89: mpBriefcaseName.Name = “BriefcaseName”;
    90: mpBriefcaseName.HasGet = true;
    91: mpBriefcaseName.GetStatements.Add(new CodeSnippetExpression(“return m_BriefcaseName”));
    92: mpBriefcaseName.HasSet = true;
    93: mpBriefcaseName.SetStatements.Add(new CodeSnippetExpression(“m_BriefcaseName = value”));
    94: ctd.Members.Add(mpBriefcaseName);

    This code will create the property handlers for the member fields defined earlier. These properties will then be attached to the Briefcase Class so the code generator will know who should own these properties when the source code is generated. Line 86 defines a conceptual placeholder for a CodeMemberProperty Class. This is the CodeDom class used to represent a class property. Line 87 sets the scope of the property to private. Line 88 sets the data type of the property to contains a “string” value. Line 89 sets the name of the property to “BriefcaseName”. This is the name other developers will reference when they want to read the contents of our m_BriefcaseName member field. Line 90 informs the code generator the value of the “BriefcaseName” property can be read at runtime. Line 91 creates the code that will return the value of the “m_BriefcaseName” member field value. Line 92 informs the code generator the value of the “BriefcaseName” property can be modified at runtime. Line 93 creates the code that will modify the contents of the “m_BriefcaseName” member variable using the new value.


    Now that we are done creating the structure of the new class property, we need to attach this property to the class they belong to. Line 94 attaches the “BriefcaseName” property to the “Briefcase” Class. In this way, when the code generator reads the CodeDom Tree we have been creating, it knows that this property belongs to the Briefcase Class and will generate the appropriate C# source code in the correct place, belonging to the correct object.



    96: //Create Class BriefcaseTitle Property
    97: CodeMemberProperty mpBriefcaseTitle = new CodeMemberProperty();
    98: mpBriefcaseTitle.Attributes = MemberAttributes.Private;
    99: mpBriefcaseTitle.Type = new CodeTypeReference(“string”);
    100: mpBriefcaseTitle.Name = “BriefcaseTitle”;
    101: mpBriefcaseTitle.HasGet = true;
    102: mpBriefcaseTitle.GetStatements.Add(new CodeSnippetExpression(“return m_BriefcaseTitle”));
    103: mpBriefcaseTitle.HasSet = true;
    104: mpBriefcaseTitle.SetStatements.Add(new CodeSnippetExpression(“m_BriefcaseTitle = value”));
    105: ctd.Members.Add(mpBriefcaseTitle);

    This code will create the property handlers for the member fields defined earlier. These properties will then be attached to the Briefcase Class so the code generator will know who should own these properties when the source code is generated. Line 97 defines a conceptual placeholder for a CodeMemberProperty Class. This is the CodeDom class used to represent a class property. Line 98 sets the scope of the property to private. Line 99 sets the data type of the property to contain a “string” value. Line 100 sets the name of the property to “BriefcaseTitle”. This is the name other developers will reference when they want to read the contents of our m_BriefcaseTitle member field. Line 101 informs the code generator the value of the “BriefcaseTitle” property can be read at runtime. Line 102 creates the code that will return the value of the “m_BriefcaseTitle” member field value. Line 103 informs the code generator the value of the “BriefcaseTitle” property can be modified at runtime. Line 104 creates the code that will modify the contents of the “m_BriefcaseTitle” member variable using the new value. Now that we are done creating the structure of the new class property, we need to attach this property to the class they belong to. Line 105 attaches the “BriefcaseTitle” property to the “Briefcase” Class. In this way, when the code generator reads the CodeDom Tree we have been creating, it knows that this property belongs to the Briefcase Class and will generate the appropriate C# source code in the correct place, belonging to the correct object.



    107: //Create Class BriefcaseID Property
    108: CodeMemberProperty mpBriefcaseID = new CodeMemberProperty();
    109: mpBriefcaseID.Attributes = MemberAttributes.Private;
    110: mpBriefcaseID.Type = new CodeTypeReference(“int”);
    111: mpBriefcaseID.Name = “BriefcaseID”;
    112: mpBriefcaseID.HasGet = true;
    113: mpBriefcaseID.GetStatements.Add(new CodeSnippetExpression(“m_BriefcaseID”));
    114: mpBriefcaseID.HasSet = true;
    115: mpBriefcaseID.SetStatements.Add(new CodeSnippetExpression(“m_BriefcaseID = value”));
    116: ctd.Members.Add(mpBriefcaseID);

    This code will create the property handlers for the member fields defined earlier. These properties will then be attached to the Briefcase Class so the code generator will know who should own these properties when the source code is generated. Line 108 defines a conceptual placeholder for a CodeMemberProperty Class. This is the CodeDom class used to represent a class property. Line 109 sets the scope of the property to private. Line 110 sets the data type of the property to contain a “int” value. Line 111 sets the name of the property to “BriefcaseID”. This is the name other developers will reference when they want to read the contents of our m_BriefcaseID member field. Line 112 informs the code generator the value of the “BriefcaseID” property can be read at runtime. Line 113 creates the code that will return the value of the “m_BriefcaseID” member field value. Line 114 informs the code generator the value of the “BriefcaseID” property can be modified at runtime. Line 115 creates the code that will modify the contents of the “m_BriefcaseID” member variable using the new value. Now that we are done creating the structure of the new class property, we need to attach this property to the class they belong to. Line 116 attaches the “BriefcaseID” property to the “Briefcase” Class. In this way, when the code generator reads the CodeDom Tree we have been creating, it knows that this property belongs to the Briefcase Class and will generate the appropriate C# source code in the correct place, belonging to the correct object.



    118: //Create Class BriefcaseSection Property
    119: CodeMemberProperty mpBriefcaseSection = new CodeMemberProperty();
    120: mpBriefcaseSection.Attributes = MemberAttributes.Private;
    121: mpBriefcaseSection.Type = new CodeTypeReference(“int”);
    122: mpBriefcaseSection.Name = “BriefcaseSection”;
    123: mpBriefcaseSection.HasGet = true;
    124: mpBriefcaseSection.GetStatements.Add(new CodeSnippetExpression
    125: (“return m_BriefcaseSectionID”));
    126: mpBriefcaseSection.HasSet = true;
    127: mpBriefcaseSection.SetStatements.Add(new CodeSnippetExpression
    128: (“m_BriefcaseSectionID = value”));
    129: ctd.Members.Add(mpBriefcaseSection);

    This code will create the property handlers for the member fields defined earlier. These properties will then be attached to the Briefcase Class so the code generator will know who should own these properties when the source code is generated. Line 119 defines a conceptual placeholder for a CodeMemberProperty Class. This is the CodeDom class used to represent a class property. Line 120 sets the scope of the property to private. Line 121 sets the data type of the property to contain a “int” value. Line 122 sets the name of the property to “BriefcaseSectionID”. This is the name other developers will reference when they want to read the contents of our m_BriefcaseSectionID member field. Line 123 informs the code generator the value of the “BriefcaseSectionID” property can be read at runtime. Line 124 creates the code that will return the value of the “m_BriefcaseSectionID” member field value. Line 126 informs the code generator the value of the “BriefcaseSectionID” property can be modified at runtime. Line 127 creates the code that will modify the contents of the “m_BriefcaseSectionID” member variable using the new value. Now that we are done creating the structure of the new class property, we need to attach this property to the class they belong to. Line 129 attaches the “BriefcaseID” property to the “Briefcase” Class. In this way, when the code generator reads the CodeDom Tree we have been creating, it knows that this property belongs to the Briefcase Class and will generate the appropriate C# source code in the correct place, belonging to the correct object.



    131: //Create Class BriefcaseFolder Property
    132: CodeMemberProperty mpBriefcaseFolder = new CodeMemberProperty();
    133: mpBriefcaseFolder.Attributes = MemberAttributes.Private;
    134: mpBriefcaseFolder.Type = new CodeTypeReference(“int”);
    135: mpBriefcaseFolder.Name = “BriefcaseFolder”;
    136: mpBriefcaseFolder.HasGet = true;
    137: mpBriefcaseFolder.GetStatements.Add(new CodeSnippetExpression(“return m_BriefcaseFlderID”));
    138: mpBriefcaseFolder.HasSet = true;
    139: mpBriefcaseFolder.SetStatements.Add(new CodeSnippetExpression(“m_BriefcaseFolderID = value”));
    140: ctd.Members.Add(mpBriefcaseFolder);

    This code will create the property handlers for the member fields defined earlier. These properties will then be attached to the Briefcase Class so the code generator will know who should own these properties when the source code is generated. Line 132 defines a conceptual placeholder for a CodeMemberProperty Class. This is the CodeDom class used to represent a class property. Line 133 sets the scope of the property to private. Line 134 sets the data type of the property to contain a “int” value. Line 135 sets the name of the property to “BriefcaseFolder”. This is the name other developers will reference when they want to read the contents of our m_BriefcaseFolderID member field. Line 136 informs the code generator the value of the “BriefcaseFolder” property can be read at runtime. Line 137 creates the code that will return the value of the “m_BriefcaseFolderID” member field value. Line 138 informs the code generator the value of the “BriefcaseFolder” property can be modified at runtime. Line 139 creates the code that will modify the contents of the “m_BriefcaseFolderID” member variable using the new value. Now that we are done creating the structure of the new class property, we need to attach this property to the class they belong to. Line 140 attaches the “BriefcaseFolder” property to the “Briefcase” Class. In this way, when the code generator reads the CodeDom Tree we have been creating, it knows that this property belongs to the Briefcase Class and will generate the appropriate C# source code in the correct place, belonging to the correct object.


    142: //Create Class BriefcaseItem Property
    143: CodeMemberProperty mpBriefcaseItem = new CodeMemberProperty();
    144: mpBriefcaseItem.Attributes = MemberAttributes.Private;
    145: mpBriefcaseItem.Type = new CodeTypeReference(“string”);
    146: mpBriefcaseItem.Name = “BriefcaseItem”;
    147: mpBriefcaseItem.HasGet = true;
    148: mpBriefcaseItem.GetStatements.Add(new CodeSnippetExpression(“return m_BriefcaseItemID”));
    149: mpBriefcaseItem.HasSet = true;
    150: mpBriefcaseItem.SetStatements.Add(new CodeSnippetExpression(“m_BriefcaseItemID = value”));
    151: ctd.Members.Add(mpBriefcaseItem);

    This code will create the property handlers for the member fields defined earlier. These properties will then be attached to the Briefcase Class so the code generator will know who should own these properties when the source code is generated. Line 143 defines a conceptual placeholder for a CodeMemberProperty Class. This is the CodeDom class used to represent a class property. Line 144 sets the scope of the property to private. Line 145 sets the data type of the property to contain a “string” value. Line 146 sets the name of the property to “BriefcaseItem”. This is the name other developers will reference when they want to read the contents of our m_BriefcaseItemID member field. Line 147 informs the code generator the value of the “BriefcaseItem” property can be read at runtime. Line 148 creates the code that will return the value of the “m_BriefcaseItemID” member field value. Line 149 informs the code generator the value of the “BriefcaseItem” property can be modified at runtime. Line 150 creates the code that will modify the contents of the “m_BriefcaseItemID” member variable using the new value. Now that we are done creating the structure of the new class property, we need to attach this property to the class they belong to. Line 151 attaches the “BriefcaseItem” property to the “Briefcase” Class. In this way, when the code generator reads the CodeDom Tree we have been creating, it knows that this property belongs to the Briefcase Class and will generate the appropriate C# source code in the correct place, belonging to the correct object.



    153: //Create Class GetBriefcaseName Method
    154: CodeMemberMethod mtd1 = new CodeMemberMethod();
    155: mtd1.Name = “GetBriefcaseName”;
    156: mtd1.ReturnType = new CodeTypeReference(“String”);
    157: mtd1.Attributes = MemberAttributes.Public;
    158: mtd1.Statements.Add(new CodeSnippetStatement(“return BriefcaseName;”));
    159: ctd.Members.Add(mtd1);

    This code will create a member method for our Briefcase Class called “GetBriefcaseName.” After configuring the method with various attributes, the conceptual placeholder for the method and its attributes is added to the Briefcase CodeDom tree. Line 154 creates a conceptual placeholder for a new class member method by using the CodeMemberMethod Class. Now that we have identified the conceptual placeholder as a class member method, we need to configure the method definition. Line 155 sets the name of our new method to be “GetBriefcaseName”. Line 156 sets the data type of the return value of our method to be of type “string.” Line 157 sets the scope of our method to be “public”.


    Line 158 inserts the code necessary to return a value from our function. Now that our new member method has been fully configured we add it to our Briefcase Class. Line 159 does this for us.



    161: //Create Class GetBriefcaseTitle Method
    162: CodeMemberMethod mtd2 = new CodeMemberMethod();
    163: mtd2.Name = “GetBriefcaseTitle”;
    164: mtd2.ReturnType = new CodeTypeReference(“String”);
    165: mtd2.Attributes = MemberAttributes.Public;
    166: mtd2.Statements.Add(new CodeSnippetStatement(“return BriefcaseTitle;”));
    167: ctd.Members.Add(mtd2);


    This code will create a member method for our Briefcase Class called “GetBriefcaseTitle.” After configuring the method with various attributes, the conceptual placeholder for the method and its attributes is added to the Briefcase CodeDom tree. Line 162 creates a conceptual placeholder for a new class member method by using the CodeMemberMethod Class. Now that we have identified the conceptual placeholder as a class member method, we need to configure the method definition. Line 163 sets the name of our new method to be “GetBriefcaseTitle”. Line 164 sets the data type of the return value of our method to be of type “string.” Line 165 sets the scope of our method to be “public”.


    Line 166 inserts the code necessary to return a value from our function. Now that our new member method has been fully configured we add it to our Briefcase Class. Line 167 does this for us.



    169: //Create Class GetBriefcaseID Method
    170: CodeMemberMethod mtd3 = new CodeMemberMethod();
    171: mtd3.Name = “GetBriefcaseID”;
    172: mtd3.ReturnType = new CodeTypeReference(“Int”);
    173: mtd3.Attributes = MemberAttributes.Public;
    174: mtd3.Statements.Add(new CodeSnippetStatement(“return BriefcaseID;”));
    175: ctd.Members.Add(mtd3);

    This code will create a member method for our Briefcase Class called “GetBriefcaseID.” After configuring the method with various attributes, the conceptual placeholder for the method and its attributes is added to the Briefcase CodeDom tree. Line 170 creates a conceptual placeholder for a new class member method by using the CodeMemberMethod Class. Now that we have identified the conceptual placeholder as a class member method, we need to configure the method definition. Line 171 sets the name of our new method to be “GetBriefcaseID”. Line 172 sets the data type of the return value of our method to be of type “int.” Line 173 sets the scope of our method to be “public”. Line 174 inserts the code necessary to return a value from our function. Now that our new member method has been fully configured we add it to our Briefcase Class. Line 175 does this for us.



    177: //Create Class GetBriefcaseSection Method
    178: CodeMemberMethod mtd4 = new CodeMemberMethod();
    179: mtd4.Name = “GetBriefcaseSectionID”;
    180: mtd4.ReturnType = new CodeTypeReference(“Int”);
    181: mtd4.Attributes = MemberAttributes.Public;
    182: mtd4.Statements.Add(new CodeSnippetStatement(“return BriefcaseSectionID;”));
    183: ctd.Members.Add(mtd4);

    This code will create a member method for our Briefcase Class called “GetBriefcaseSection.” After configuring the method with various attributes, the conceptual placeholder for the method and its attributes is added to the Briefcase CodeDom tree. Line 178 creates a conceptual placeholder for a new class member method by using the CodeMemberMethod Class. Now that we have identified the conceptual placeholder as a class member method, we need to configure the method definition. Line 179 sets the name of our new method to be “GetBriefcaseSection”. Line 180 sets the data type of the return value of our method to be of type “int.” Line 181 sets the scope of our method to be “public”. Line 182 inserts the code necessary to return a value from our function. Now that our new member method has been fully configured we add it to our Briefcase Class. Line 183 does this for us.



    185: //Create Class GetBriefcaseFolder Method
    186: CodeMemberMethod mtd5 = new CodeMemberMethod();
    187: mtd5.Name = “GetBriefcaseFolderID”;
    188: mtd5.ReturnType = new CodeTypeReference(“Int”);
    189: mtd5.Attributes = MemberAttributes.Public;
    190: mtd5.Statements.Add(new CodeSnippetStatement(“return BriefcaseFolderID;”));
    191: ctd.Members.Add(mtd5);

    This code will create a member method for our Briefcase Class called “GetBriefcaseFolder.” After configuring the method with various attributes, the conceptual placeholder for the method and its attributes is added to the Briefcase CodeDom tree. Line 186 creates a conceptual placeholder for a new class member method by using the CodeMemberMethod Class. Now that we have identified the conceptual placeholder as a class member method, we need to configure the method definition. Line 187 sets the name of our new method to be “GetBriefcaseFolderID”. Line 188 sets the data type of the return value of our method to be of type “int.” Line 189 sets the scope of our method to be “public”. Line 190 inserts the code necessary to return a value from our function. Now that our new member method has been fully configured we add it to our Briefcase Class. Line 191 does this for us.



    193: //Create Class GetBriefcaseItem Method
    194: CodeMemberMethod mtd6 = new CodeMemberMethod();
    195: mtd6.Name = “GetBriefcaseItemID”;
    196: mtd6.ReturnType = new CodeTypeReference(“Int”);
    197: mtd6.Attributes = MemberAttributes.Public;
    198: mtd6.Statements.Add(new CodeSnippetStatement(“return BriefcaseItemID;”));
    199: ctd.Members.Add(mtd6);

    This code will create a member method for our Briefcase Class called “GetBriefcaseItem.” After configuring the method with various attributes, the conceptual placeholder for the method and its attributes is added to the Briefcase CodeDom tree. Line 194 creates a conceptual placeholder for a new class member method by using the CodeMemberMethod Class. Now that we have identified the conceptual placeholder as a class member method, we need to configure the method definition. Line 195 sets the name of our new method to be “GetBriefcaseItemID”. Line 196 sets the data type of the return value of our method to be of type “int.” Line 197 sets the scope of our method to be “public”. Line 198 inserts the code necessary to return a value from our function. Now that our new member method has been fully configured we add it to our Briefcase Class. Line 199 does this for us.



    201: //Generate Source Code File
    202: cscg.GenerateCodeFromNamespace(cnsCodeDom, sw, cop);

    This code will take the CodeDom tree we have been building up to this point, feed it into the C# Code Generator Engine, and output our C# source code to a file on disk using the StreamWriter we created earlier. Since the CodeDom Tree has been built successfully, we will now generate the C# source code. Line 202 generates our C# source code from the CodeDom Tree we have been building upon so far, which has the base “using” statement as its root statement. To generate source code using the statements in Line 202, we need to feed the C# code generator engine three parameters: the CodeDom Tree containing the definitions of the constructs we wish to generate source code for, the StreamWriter to use for physically writing the bits to disk, and any code generation options to use when writing the bits to disk – such as how many spaces to indent when indenting and which bracing style to use since we are targeting C# as our target language.



    204: //Close StreamWriter
    205: sw.Close();
    206: s.Close();

    Since we are finished creating our physical C# source code file on disk, we need to set both the Stream and the StreamWriter objects for garbage collection by the CLR. To do this, we simply call the close() method on the Stream and the StreamWriter objects respectively. Just to reflect, we needed to create a Stream object because we needed a way to manage the bits being written to the disk. We needed to create a StreamWriter object to handle writing the physical bits to the disk.


    Figure 1.6 contains the code that is generated by the code listed in Figure 1.5. You may notice one odd thing regarding the difference in length between the listings. Did you find it? If you said that it took 209 lines of code in Figure 1.5, to create 99 lines of source code in Figure 1.6, you are correct. You may find that there is a significant difference in the number of lines used to create your own code generators versus the number of lines of code created by your code generator. A few possible reasons for this discrepancy could be your coding style (I prefer creating the object, then configuring it which requires significantly more code lines than combining programming constructs on one line), the complexity of the objects you are trying to create with your code generator, or a profuse use of the “Snippet” classes because needed functionality is missing from the present CodeDom Implementation.

    Whatever your reasons for wanting to investigate the System.CodeDom and System.CodeDom.Compiler Namespaces of the Microsoft.NET Framework, I trust you learned a little about why the CodeDom can be a powerful tool for your applications, and what kinds of pitfalls you will encounter when using the current implementation of CodeDom as it stands today. The introduction of the Microsoft.NET Framework, and Visual Studio.NET, truly represent more than an evolution in development skills and methodologies for Microsoft Developers – it represents a revolution in how software is designed, written and deployed. As you journey deeper into the CodeDom Namespaces, you will see the power and the potential of this complex, yet conceptually easy to use collection of code generation classes. And as you encounter missing functionality, be sure to drop Microsoft(c) your wishes for the next release of the CodeDom Namespaces at mswish@microsoft.com. Microsoft(c) monitors this e-mail alias and appreciates your feedback about how to improve the CodeDom Namespaces.



    Conclusion



    In this article we learned about tree data structures and how they conceptually work. Trees contain nodes, which are conceptual placeholders for data. Nodes are linked together to form a tree structure in memory which is why we call them trees. Trees are searched by applying a searching algorithm to the tree to locate data.

    The CodeDom provides the following advantages:



    1. A single model for rendering source code.
    2. A Program can be dynamically created, compiled, and executed at runtime.
    3. A Language independent object model
    4. A translation between source code files written in different languages.


    The CodeDom Namespaces do not fully support all programming constructs presently. To represent constructs not presently supported in CodeDom you can use the “Snippet” classes. These classes allow you to insert literal lines of source code into a compile unit. The classes available for handling snippets include: the CodeSnippetCompileUnit, CodeSnippetExpression, CodeSnippetStatement, and CodeSnippetTypeMember classes.


    There are two main CodeDom Namespaces: the System.CodeDom and System.CodeDom.Compiler Namespaces. The System.CodeDom Namespace is used to produce the CodeDom Tree, while the System.CodeDom.Compiler Namespace is used to process the CodeDom Tree.

    Other related CodeDom Technologies include: Reflection, Emitting and Custom Attributes.



    About the Author



    Brian has been working in the IT Industry since 1995. He currently lives in Denver, Colorado. His specialties include Software Design, Systems Analysis and Design, and Backend Server Development using VB, COM, MTS, ADO, SQL, T-SQL, and SQL Server 2000. He has worked for Microsoft as an ASP.NET Support Engineer during the worldwide launch of the Visual Studio.NET Product. He has used Visual Studio.NET since Beta 0 and has become specialized in VB.NET and the System.CodeDom and System.CodeDom.Compiler Namespaces of the Microsoft.NET Framework. He holds a B.S, Computer Science Degree from Evangel University. His research interests lie in language development for the .NET Framework, and IDE Productivity Add-Ins and Windows Services for .NET. Brian can be reached at VBAnswerGuy@adelphia.net. He is in the process of developing an academic textbook for teaching Visual Basic.NET to college students. If interested in his book, please contact him at: VBAnswerGuy@adelphia.net. When not burning up the power supply in Colorado, Brian enjoys road trips, hiking, fishing and spending time with his family in the mountains of Colorado.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read