Generating .Net Source Code

(by Joe Williams for Blayd Software)

Note: this article relates to the .Net Framework version 2.0, some of the classes and class members referenced in this article were not available in earlier framework versions.

See Also

.Net Framework source code generation services and products provided by Blayd Software.

CodeDOM Helper Library

The CodeDOM Helper Library provides an abstraction layer over the types in the System.CodeDom namespace. Developers can use the CodeDOM Helper library types to simplify the process of creating a Code Document Object Model and can easily generate source code from the model in any, supported, .Net Framework language.

CodeDOM Helper Class Library Component

Online Source Code Generation

We are currently offering a selection of generic and non-generic collection base classes from which you can generate your own, closed constructed, derived collections. There is also a selection of, ready to run, open constructed, generic comparers to choose from.

Online .Net Framework Source Code Generator

Introduction

The .Net Framework enables different, supported, languages to be compiled into a common, intermediate language (MSIL) that can then be either pre-compiled or just-in-time compiled to machine code. The framework's Common Type System (CTS) enables objects written in one language to interact with and be used by objects written in another language and the Common Language Runtime (CLR) provides a managed environment in which objects are executed.

We appreciate that the previous paragraph is a very simplistic overview of the .Net Framework, however, if you have been developing with the framework for any time you will or should be familiar with the basic principals. If you are new to the framework there are plenty of resources available, books, magazines and online that explain the fundamentals of the .Net Framework in far more detail than we can in this article.

The .Net Framework includes a sub-system, called the Code Document Object Model (CodeDOM), that enables the language-independent modelling of source code. The model allows developers to emit source code, at runtime, in any supported .Net language, from a single CodeDOM. The System.CodeDom namespace contains the types used to construct a Code Document Object Model or source code object graph and the System.CodeDom.Compiler namespace contains the types used to generate and compile the source code in a, supported, .Net Framework language. We will be concentrating on constructing a source code object graph and generating source code in this article rather than compiling and emitting assemblies, however, once you have generated the source code, compiling it to an assembly is fairly straight forward.

The ability to generate source code at runtime can be useful in many situations, one of the most obvious being the development of productivity tools to automate the production of boilerplate code. Custom control and component designers may also have to generate source in order to serialize their state to allow the control or component to be created and initialized at runtime. Tools such as data access layer and object relational mapping generators are plentiful and presumably, therefore, popular. UML modelling applications and Class designers are also plentiful and both of these types of application, typically, have extensive code generation functionality. The recurring theme in virtually all applications of source code generation seems to be productivity, however accuracy and consistency also have important parts to play. Automating the production of what is essentially boilerplate source code or generating source code from another source that is easier to create or does not require programming skills to create, such as UML or XML created in a user friendly UI, for example, can make the effort required to master the Code Document Object Model worthwhile.

The sample code used in this article is CSharp as there is very little difference between CSharp and Visual Basic in most cases. Where the source code generated from the sample code is shown we will show both CSharp and Visual Basic, in most cases, as this serves to emphasise how the same generation code can produce different results in different code providers. You can use any .Net language to write a code generator and you can output source code in any .Net language, for example, a code generator written in CSharp can output CSharp and Visual Basic source code and a code generator written in Visual Basic can output Visual Basic and CSharp source code.

Code Providers

The CodeDOM provides a language-independent representation of source code and language-specific code providers are used to translate the source code object graph into language-specific source code. The System.CodeDom.Compiler.CodeDomProvider abstract base class is extended by all of the framework's built-in language-specific code providers, for example, both the Microsoft.CSharp.CSharpCodeProvider class and the Microsoft.VisualBasic.VBCodeProvider class extend CodeDomProvider. Therefore, to generate source code from your source code object graph, you first have to create the relevant code provider instance. There are various ways to create a code provider instance, you can, for example, create the relevant provider instance directly if you are only generating source code in one language. However, if you are supporting more than one .Net Framework language you will, typically, have to create a provider instance dynamically using the language name as specified or selected by the user.

You can use the static CodeDomProvider.GetAllCompilerInfo method to enumerate the code providers installed on a machine as follows:

ExpandEnumerate code providers:
ExpandThe code above produces the following output on one of our test machines:

If you run the enumeration sample code on your own machine your output may vary from the example above as you may have fewer or more code providers installed and configured on your machine. Developers and compiler vendors can install and configure their own language specific providers by specifying the provider settings in the system.codedom section of the machine.config file.

You can use the static CodeDomProvider.IsDefinedLanguage method to check if there is a provider for a specific language. Most providers define more than one language name, as demonstrated in the sample output above, for example, you can create a CSharp provider instance using "c#", "cs" or "csharp".

ExpandTest for language support:
ExpandThe code above produces the following output:

As demonstrated above the language name comparison is case insensitive, so you can, for example, specify either "cs" or "CS" as the language name.

When you have a language name and you have confirmed that the name is defined by an installed code provider you can use the language name in a call to the static CodeDomProvider.CreateProvider method to create an instance of the relevant provider.

Create a code provider instance:

CodeDomProvider provider = CodeDomProvider.CreateProvider("CS");

The CodeDomProvider.CreateProvider method should only be used when you do not know until runtime which language you will be generating source code in. If you are only ever going to be generating source code in one specific language or if you are only going to be generating source code in one or two known languages then it is better to create the relevant code provider explicitly, for example, if you are only generating CSharp source code then explicitly create an instance of the CSharpCodeProvider class. The reason for this is due to the way that the CodeDomProvider.CreateProvider method works, if there is more than one code provider available for the specified language name, the method returns a provider instance for the last matching configuration element. In most cases this won't matter, however, if you want to be sure that you are using a particular provider for a specific language then create the provider instance yourself.

The reason that we are discussing code providers before we have discussed how to create a source code object graph is to highlight one or two gotchas that you might run into when using a code provider to generate your source code. As mentioned previously the CodeDOM is a language-independent representation of the source code and a language specific code provider is used to actually generate the source code from the CodeDOM. When building a source code object graph you will be specifying the Type of code elements repeatedly, for example, the type of a field, the type of a parameter, the type of a property, etc. If you are intending to generate source code in more than one language and to some extent even if you are only intending to generate in one language, care needs to be taken when specifying types. The following examples show various ways in which a field can be created in the source code object graph.

ExpandCreating a set of example private fields:

The generated CSharp source code including the above example fields:

public class TestClass
{
   private @int _testField1;
   private int _testField2;
   private int _testField3;

   private @int _testField4;
   private int _testField5;
   private int _testField6;
}

The generated Visual Basic source code including the above example fields:

Public Class TestClass
   Private _testField1 As int
   Private _testField2 As Integer
   Private _testField3 As Integer

   Private _testField4 As int
   Private _testField5 As Integer
   Private _testField6 As Integer
End Class

Don't worry too much about where the class came from or how the fields were added to it for now, we will be discussing that in detail later in the article. The point to note from this example is how easy it is to get it wrong, as neither the CSharp or Visual Basic generated code above will compile (assuming that the relevant project does not contain a class named "int").

When specifying the type of a code element we strongly recommend that where possible you use a Type e.g. typeof(int) (GetType(Integer) in Visual Basic). If you have to use a string to specify the type of a code element and you may have to in some circumstances we recommend that you use a namespace qualified type name e.g. "System.Int32", "System.String", "System.DateTime", etc. Using this technique enables the relevant code provider to translate the code element correctly and output valid source code in the relevant language.

The types in the System.CodeDom namespace are used to build a language-independent CodeDOM or source code object graph and language-specific code providers are used to translate the source code object graph into source code for their respective language. The System.CodeDom types provide good support for modelling types e.g. class, interface, struct, etc. and good support for modelling type members and member signatures e.g. fields, properties, methods, etc. but the support for modelling the code structures used, for example, in the body of a method is pretty basic. Therefore, it is very easy to generate the source code for a class template or for an interface, however, creating the code body for anything other than a very basic method can be challenging. When you think about the multitude of statements, expression and code structures supported by a modern language and compiler and then consider whether a particular feature is only supported by some of the languages or is supported by all of the languages, it soon becomes obvious that including a type or types to model every possible feature is asking a lot.

The System.CodeDom namespace contains several code snippet types that can be used to include literal source code in the CodeDOM or source code object model. These types store a literal source code segment in its original form as a string which is then output, as is, when source code is generated from the source code object model. Using one of these types it is possible to output any source code, therefore, you can generate complex code structures or method bodies with ease. However, you guessed it, there is a catch, code snippets are language specific, they will not be translated by a code provider, therefore, they have to be in the target language. If you are developing a code generator that only supports one .Net language, literal code snippets are not a problem, if your generator supports more than one language then literal code snippets can be a real pain. Including literal code snippets for every .Net language is not, typically, a viable option because as soon as a new code provider arrives on the market, your code generator is compromised and no longer works with all .Net languages. Ideally, when using literal code snippets you should have a fallback position, for example, you may create language specific snippets for CSharp and Visual Basic, the most commonly used .Net languages and also include code elements created using the types available in System.CodeDom for any language other than CSharp or Visual Basic.

When you are developing a code generator you have to consider how you are going to manage user expectations. Typically, code generators are used by developers and a developer may well expect the generated source code to use the techniques that they would use themselves. For example, when iterating through a collection a developer would, probably, expect the generated code to use a foreach (For Each in Visual Basic) loop, however, you can't generate a foreach loop without resorting to a literal code snippet. In our experience, when developing a code generator compromises have to be made, in some places the generated source code will not be as "pretty" as it could be and occasionally the generated source code will not be as efficient as it could be but in all cases the generated source must compile and work as expected and produce accurate results.

If you are developing a code generator that supports multiple .Net languages we strongly recommend that you perform some validation after creating a code provider instance but before generating the source code. The CodeDomProvider base class defines several methods, that should be overridden by a concrete code provider class, that can be used to check for language feature support and identifier validation and creation.

CreateEscapedIdentifier
The CreateEscapedIdentifier method checks if the passed in identifier string is a reserved or key word in the relevant language. If it is, the method returns the language-specific escaped version of the passed in value. For example, passing in the value "class" to the CSharp code provider would result in the value "@class" being returned whilst the Visual Basic code provider would return "[class]". If the passed in value is not a reserved or key word it is returned unaltered.
CreateValidIdentifier
The CreateValidIdentifier method checks if the passed in identifier string is a reserved or key word in the relevant language. If it is, the method attempts to alter the identifier to resolve the conflict. The method usually adds an underscore character ("_") to the start of the identifier, for example, passing in the value "class" to the CSharp or the Visual Basic code provider would, typically, result in the value "_class" being returned. If the passed in value is not a reserved or key word it is returned unaltered.
IsValidIdentifier
The IsValidIdentifier method checks if the passed in identifier string is a reserved or key word in the relevant language. If it is, the method returns false, if it is not, the method returns true.
Supports
The Supports method checks if the relevant language supports the features passed in. The method accepts one or more of the System.CodeDom.Compiler.GeneratorSupport enumerated values. The GeneratorSupport enumeration is a bit field i.e. it is decorated with the FlagsAttribute, therefore the passed in value can specify one or more language features. The method returns true if all of the specified language features are supported and false if, at least, one specified feature is not supported.

Code Document Object Model

The Code Document Object Model (CodeDOM) is implemented as a hierarchy with a single System.CodeDom.CodeCompileUnit class or a class derived from CodeCompileUnit at its root.

Some of the main classes that we will be discussing in this article fit into the CodeDOM as depicted in Figure 1 below:

CodeDOM Extract

Figure 1

The CodeCompileUnit class has a Namespaces property that references a collection of CodeNamespace instances, therefore a CodeCompileUnit can contain one or more namespaces.

The CodeNamespace class has a Types property that references a collection of CodeTypeDeclaration instances, therefore a CodeNamespace can contain one or more types. The CodeTypeDeclaration class represents a type i.e. class, enum, interface or stuct.

The CodeTypeDeclaration class has a Members property that references a collection of CodeTypeMember instances, therefore a CodeTypeDeclaration can contain one or more members. The CodeTypeMember class is a base class from which more specific types of member are derived e.g. CodeTypeMemberField, CodeTypeMemberProperty, CodeTypeMemberMethod, etc.

Specialized CodeTypeMember derived classes that can have a code body e.g. CodeTypeMemberMethod, typically, have a Statements property that references a collection of CodeStatement instances. The CodeTypeMemberProperty class has a GetStatements property for the getter statements and a SetStatements property for the setter statements.

The CodeDOM is more complex than we have shown here, we have concentrated on the classes that you will, typically, use most when generating source code.

Generating a Class

To generate a type we have to create a CodeCompileUnit instance, add a CodeNamespace instance to it and then create a CodeTypeDeclaration instance and add that to the namespace.

ExpandSample code for generating a class:

The generated CSharp source code for the example Customer class:

namespace Samples {
    using System;

    public class Customer {
    }
}

The generated Visual Basic source code for the example Customer class:

Imports System

Namespace Samples

    Public Class Customer
    End Class
End Namespace

The interesting and possibly frustrating thing about both the CSharp and Visual Basic generated source code is that neither is really what you might expect! A CSharp developer will, probably be more used to the using statement being above the namespace statement and a Visual Basic developer, probably, did not want a sub-namespace named "Samples". However, both the CSharp and Visual Basic generated source code is valid and will compile.

When using Visual Studio, adding a Namespace statement to a Visual Basic class file, typically, creates a sub-namespace as the main or root namespace is usually specified at the project level. Therefore, typically, when generating the source code for a Visual Basic class we would not want or need to include a Namespace statement, although we would need the option of including it when necessary. There is a very simple tweak that we can make to the generation code to accommodate an optional namespace statement, we must have at least one namespace in the source code object graph but it can have an empty name and if it does have an empty name it won't appear in the generated source code.

ExpandSample code for generating a class without a namespace statement:

The generated Visual Basic source code for the sample code above:

Imports System

Public Class Customer
End Class

Although this a language-specific tweak, as we would not want to generate a CSharp class without a namespace statement, it is fairly easy to implement if the code generator is providing the user with a choice of generation languages. When the user chooses to generate Visual Basic source code the component that is generating the source code can be passed Empty for the namespace value.

We can use a variation on the empty namespace tweak to get the CSharp generated class declaration to look like we would expect. However, this is language-specific tweak that will require a conditional statement in the code generator e.g. if language is CSharp apply the tweak, else don't apply the tweak.

ExpandSample code for generating a more familiar CSharp namespace and class declaration:

The generated CSharp source code for the sample code above:

using System;

namespace Samples {

    public class Customer {
    }
}

The CSharp tweak adds a "dummy" namespace with an empty name to the CodeCompileUnit, it then adds the namespace imports to the dummy namespace. From then on the code is the same as the first example, the "Samples" namespace is created and added to the CodeCompileUnit and the "Customer" class is created and added to the "Samples" namespace. As demonstrated in the sample output above the tweak generates source code that will be more familiar to CSharp developers than the first example.

Generating an Interface

As demonstrated in the previous section creating an instance of the CodeTypeDeclaration class using the type name constructor and leaving its properties at their default values generates a public class. We only need to make a small adjustment to the generation code to generate a public interface rather than a public class.

ExpandSample code for generating an interface:

The generated CSharp source code for the example IReportWriter interface:

using System;

namespace Samples {

    public interface IReportWriter {
    }
}

The generated Visual Basic source code for the example IReportWriter interface:

Imports System

Public Interface IReportWriter
End Interface

The sample generation code above creates an instance of the CodeTypeDeclaration class using the type name constructor, it then sets the IsInterface property to true to generate a public interface.

Generating a Struct

As demonstrated in the Generating a Class section creating an instance of the CodeTypeDeclaration class using the type name constructor and leaving its properties at their default values generates a public class. We only need to make a small adjustment to the generation code to generate a public struct (Structure in Visual Basic) rather than a public class.

ExpandSample code for generating a struct:

The generated CSharp source code for the example CustomerId struct:

namespace Samples {

    public struct CustomerId {
    }
}

The generated Visual Basic source code for the example CustomerId struct:

Imports System

Public Structure CustomerId
End Structure

The sample generation code above creates an instance of the CodeTypeDeclaration class using the type name constructor, it then sets the IsStruct property to true to generate a public struct.

Generating an Enum

As demonstrated in the Generating a Class section creating an instance of the CodeTypeDeclaration class using the type name constructor and leaving its properties at their default values generates a public class. We only need to make a small adjustment to the generation code to generate a public enum rather than a public class.

ExpandSample code for generating an enum:

The generated CSharp source code for the example AccountType enum:

using System;

namespace Samples {

    public enum AccountType {
    }
}

The generated Visual Basic source code for the example AccountType enum:

Imports System

Public Enum AccountType
End Enum

The sample generation code above creates an instance of the CodeTypeDeclaration class using the type name constructor, it then sets the IsEnum property to true to generate a public enum.

By default, enumerations are typed as Int32, however you can specify a different type after creating the CodeTypeDelaration instance by adding the required type to the BaseTypes property.

ExpandSample code for generating an Int64 enum:

The generated CSharp source code for the example AccountType Int64 enum:

using System;

namespace Samples {

    public enum AccountType : long {
    }
}

The generated Visual Basic source code for the example AccountType Int64 enum:

Imports System

Public Enum AccountType As Long
End Enum

Note

For details of how to add enumerated members to an enum see the Generating Enumerated Members section.

Generating an Open Generic Type

To create a generic type, we will use a class as an example here, we need to add the relevant type parameters using the CodeTypeDeclaration instance's TypeParameters property. The TypeParameters property references a collection of System.CodeDom.CodeTypeParameter instances. In the example we will just set the Name property (via the constructor) of the CodeTypeParameter instance but you can also assign any constraints for the parameter.

Note

To generate a generic interface, set the IsInterface property to true after creating the CodeTypeDelcaration instance. To generate a generic struct (Structure in Visual Basic) set the IsStruct property to true.

ExpandSample code for generating a generic class:

The generated CSharp source code for the example generic ReportWriter class:

using System;

namespace Samples {

    public class ReportWriter<T>
    {
    }
}

The generated Visual Basic source code for the example generic ReportWriter class:

Imports System

Public Class ReportWriter(Of T)
End Class

The sample generation code above creates an instance of the CodeTypeDeclaration class using the type name constructor, it then adds a single type parameter which, by convention, is named "T" to define a single letter type parameter.

Specifying the Visibility and Scope of a Type

So far we have been creating and generating public types e.g. class, interface, struct, etc. which will, typically, be the option required most often. We can now move on to demonstrate how to specify the visibility and scope of a type, both of which are specified using the attributes of the CodeTypeDeclaration class.

Sounds simple, doesn't it? Not so, the CodeTypeDeclaration class inherits an Attributes property from its CodeTypeMember base class but it also implements its own TypeAttributes property. The Attributes property is typed as MemberAttributes which is found in the System.CodeDom namespace whilst the TypeAttributes property is typed as TypeAttributes which is found in the System.Reflection namespace. To complicate matters further these enumerated types use different terminology to define the visibility of a type, MemberAttributes uses the term "Access" to specify public, private, family, etc. whilst TypeAttributes uses the term "Visibility" to specify the same values.

Note

When setting the attributes of the CodeTypeDeclaration class you should use the TypeAttributes property and therefore specify the attributes using the System.Reflection.TypeAttributes enumeration. That is why we are using the term "Visibility" in this section whereas we will use the term "Access" when discussing the attributes of type members!

ExpandSample code for generating an internal class:

The generated CSharp source code for the example internal Customer class:

using System;

namespace Samples {

    internal class Customer {
    }
}

The generated Visual Basic source code for the example internal Customer class:

Imports System

Friend Class Customer
End Class

When setting the visibility flags of the CodeTypeDeclaration class you must use the VisibilityMask enumerated value to mask out the visibility flags before setting the new flags otherwise you will clear all of the other flags.

The TypeAttributes property is also used to specify the scope of a type e.g. abstract, sealed. etc.

ExpandSample code for generating a public abstract class:

The generated CSharp source code for the example abstract Customer class:

using System;

namespace Samples {

    public abstract class Customer {
    }
}

The generated Visual Basic source code for the example abstract Customer class:

Imports System

Public MustInherit Class Customer
End Class
ExpandSample code for generating a public sealed class:

The generated CSharp source code for the example sealed Customer class:

using System;

namespace Samples {

    public sealed class Customer {
    }
}

The generated Visual Basic source code for the example sealed Customer class:

Imports System

Public NotInheritable Class Customer
End Class

Scope attributes can be set directly with the TypeAttributes enumeration, however this is not the case when using the MemberAttributes enumeration to set the scope flags on a type member, you have to use the MemberAttributes.ScopeMask enumerated value to avoid clearing existing flags.

Specifying the Base Types of a Type

To specify the base type or types of a type, which can be a class and/or one or more interfaces, we need to add one or more System.CodeDom.CodeTypeReference instances to the CodeTypeDeclaration instance's BaseTypes property. The BaseTypes property references a collection of CodeTypeReference instances.

ExpandSample code for generating a class with that inherits a base class:

The generated CSharp source code for the example class with inherited base class:

using System;

namespace Samples {

    public class Manager : Employee {
    }
}

The generated Visual Basic source code for the example class with inherited base class:

Imports System

Public Class Manager
    Inherits Employee
End Class
ExpandSample code for generating a class that inherits an interface:

The generated CSharp source code for the example class with inherited interface:

using System;

namespace Samples {

    public class Employee : System.IComparable {
    }
}

The generated Visual Basic source code for the example class with inherited interface:

Imports System

Public Class Employee
    Implements System.IComparable
End Class

Note

Generating source code for multiple languages:

When specifying just an interface as a base type, you should use the Type (use typeof or GetType in Visual Basic) of the interface when constructing the CodeTypeReference instance. If you cannot use a Type then you should add System.Object as a base type before adding the interface, for example:

sampleClass.BaseTypes.Add(new CodeTypeReference(typeof(object)));

sampleClass.BaseTypes.Add(new CodeTypeReference("ISomeInterface"));

In the .Net Framework prior to version 2.0 you had to add an Object base class before the interface.

If you do not use this technique some language code providers will not interpret your intentions correctly.

ExpandSample code for generating a class that inherits a base class and an interface:

The generated CSharp source code for the example class with inherited base class and interface:

using System;

namespace Samples {

    public class Manager : Employee, ISupervisor {
    }
}

The generated Visual Basic source code for the example class with inherited base class and interface:

Imports System

Public Class Manager
    Inherits Employee
    Implements ISupervisor
End Class

Specifying a Generic Type As a Base Type

To specify a generic type as a base class we need to set the TypeArguments property of the CodeTypeReference instance that is to represent the generic base type. We can do this using the CodeTypeReference constructor that accepts an array of CodTypeReference instances as type arguments, as in the example below or we can construct the CodeTypeReference using the constructor that accepts a Type parameter.

ExpandSample code for generating a class that inherits a closed constructed generic base class:

The generated CSharp source code for the example class with an inherited closed constructed generic base class:

using System;
using System.Collections.ObjectModel;

namespace Samples {

    public class CustomerCollection : Collection<Customer> {
    }
}

The generated Visual Basic source code for the example class with an inherited closed constructed generic base class:

Imports System
Imports System.Collections.ObjectModel

Public Class CustomerCollection
    Inherits Collection(Of Customer)
End Class

When creating a CodeTypeReference instance that is to represent a generic type you should either use the constructor that accepts a Type parameter or the constructor that accepts an array of CodeTypeReference instances that represent the type arguments of the generic type.

Generating Nested Types

To nest a type within a type we need to create a CodeTypeDeclaration instance to represent the nested type and then add it to the Members property of the outer type. The requirement to support nested types is the reason that the CodeTypeDeclaration class derives from the CodeTypeMember class i.e. an instance of the CodeTypeDeclaration class can represent a type or it can represent a member of a type or a nested type.

ExpandSample code for generating a class that includes a nested class as a member:

The generated CSharp source code for the example class with a nested class member:

using System;

namespace Samples {

    public class Customer {

        private class Address {
        }
    }
}

The generated Visual Basic source code for the example class with a nested class member:

Imports System

Public Class Customer

    Private Class Address
    End Class
End Class

As demonstrated in the example code above it is the "visibility" of a type that identifies it as a nested type when it is added, as a member, to an outer type. In the example, we have set the TypeAttributes visibility flag to NestedPrivate but you can specify NestedAssembly, NestedFamANDAssembly, NestedFamily, NestedFamORAssembly or NestedPublic.

You can nest a struct within a class, by setting the IsStruct property of the CodeTypeDeclaration instance, representing the nested type, to true. You can also nest a class within a struct although this would be a fairly unusual requirement.

Generating a Private Enum Within a Type

Nesting, as demonstrated in the previous section, can also be used to include an enum as a private member of a type. Although, typically, an enum would be added as a public type to a namespace there are occasions where it is useful or necessary to define an enum as a private member of, for example, a class.

ExpandSample code for generating a class that includes a private nested enum as a member:

The generated CSharp source code for the example class with a private nested enum member:

using System;

namespace Samples {

    public class Customer {

        private enum AccountType {
        }
    }
}

The generated Visual Basic source code for the example class with a private nested enum member:

Imports System

Public Class Customer

    Private Enum AccountType
    End Enum
End Class

For details of how to add enumerated members to an enum see the Generating Enumerated Members section.

Generating Fields

To generate a field we need to add an instance of the CodeMemberField class to the CodeTypeDeclaration instance's Members property. Fields can be created as just a declaration or they can be declared and initialized, fields may be private (the default), protected, internal or public and may also be declared and initialized as constant.

ExpandSample code for generating fields:

The generated CSharp source code for the example class with fields:

using System;

namespace Samples {

    public class Customer {

        private object _lockTarget = new object();

        private int _id;

        private string _name;

        private const string ACCOUNT_BUSINESS = "Business";

        private const string ACCOUNT_PERSONAL = "Personal";
    }
}

The generated Visual Basic source code for the example class with fields:

Imports System

Public Class Customer

    Private _lockTarget As Object = New Object

    Private _id As Integer

    Private _name As String

    Private Const ACCOUNT_BUSINESS As String = "Business"

    Private Const ACCOUNT_PERSONAL As String = "Personal"
End Class

The example code above demonstrates generating an initialized field (_lockTarget) by assigning a CodeObjectCreateExpression instance to the CodeMemberField instance's InitExpression property. The first CodeObjectCreateExpression constructor parameter specifies the Type of the object to create, Object in the example and the second, CodeExpression array, parameter specifies the parameters required by the object's constructor, in the example above the default constructor is required so an empty array is specified.

The next two fields in the example above are simple declarations, however, you should note that the CodeMemberField constructor that accepts a Type is used to create the fields.

The final two fields in the example above are also initialized fields, however they are declared and initialized as constant fields. The CodeMemberField instance's InitExpression property is used to assign an instance of the CodePrimitiveExpression class initialized using a literal string. We then have to set the Attributes property scope flags to MemberAttributes.Const ensuring that we mask out the scope flags using the MemberAttributes.ScopeMask. If you want to create a public, rather than the default private field set the Attributes property MembersAttribute.Public access flag whilst masking out the access flags using the MemberAttributes.AccessMask.

Generating Enumerated Members

We can also use the CodeMemberField class to add members to an enumeration or enum. The type of an enumerated member is implied by the type of the enumeration, which by default is Int32. Therefore, when creating instances of the CodeMemberField class to add to an enum type we don't have to specify a type for the field, however, typically, it is just as easy to specify a type as not to, so you may as well, just remember to specify the enum type for each field.

ExpandSample code for generating enum fields:

The generated CSharp source code for the example enum with fields:

using System;

namespace Samples {

    public enum AccountType {

        Business,
        Personal,
    }
}

The generated Visual Basic source code for the example enum with fields:

Imports System

Public Enum AccountType
    Business
    Personal
End Enum

If we don't want the first enumerated member to have the value 0 (zero) which is the default, we can use the InitExpression property to assign a non-default value to the first field as follows.

ExpandSample code for generating an initialized enum field:

The generated CSharp source code for the example initialized enum field:

using System;

namespace Samples {

    public enum AccountType {

        Business = 1,
        Personal,
    }
}

The generated Visual Basic source code for the example initialized enum field:

Imports System

Public Enum AccountType
    Business = 1
    Personal
End Enum

The fields we add to an enum represent the named constants or enumerator list, however, we don't have to explicitly set the MemberAttributes.Const flag of each field. In the generated CSharp source code you may have noticed that there is a comma after the last field. Typically, when hand coding an enum a developer would not include the last comma as it is not necessary, however, it is not invalid to do so and the generated code will compile without error.

Generating Constructors

To generate a constructor we need to add an instance of the CodeConstructor class to the CodeTypeDeclaration instance's Members property. When creating the constructor we can use the BaseConstructorArgs property to pass values to the base type's constructor or the ChainedConstructorArgs property to pass values to another constructor overload. Both the BaseConstructorArgs property and the ChainedConstructorArgs property reference a collection of CodeExpression instances that can be used to specify either a specific value or a reference to a constructor parameter. The CodeConsructor instance's, inherited, Parameters property can be used to specify the constructor parameters. The Parameters property references a collection of CodeParameterDeclarationExpression instances.

ExpandSample code for generating constructors:

The generated CSharp source code for the example class with constructors:

using System;

namespace Samples {

    public class Customer {

        public Customer() :
                this(-1, string.Empty) {
        }

        public Customer(int id, string name) :
                base() {
        }
    }
}

The generated Visual Basic source code for the example class with constructors:

Imports System

Public Class Customer

    Public Sub New()
        Me.New(-1, String.Empty)
    End Sub

    Public Sub New(ByVal id As Integer, ByVal name As String)
        MyBase.New()
    End Sub
End Class

The example code above demonstrates generating a default, parameterless, constructor that passes default values to another constructor overload using the ChainedConstructorArgs property. An instance of the CodePrimitiveExpression class is used to pass the value -1 as the default ID to the other constructor overload and an instance of the CodePropertyReferenceExpression class is used to pass the value of the string.Empty field as the default name to the other constructor overload. An instance of the CodeTypeReferenceExpression class is used to specify the string.Empty field for the CodePropertyReferenceExpression instance.

The second constructor is generated with two parameters by adding two instances of the CodeParameterDeclarationExpression class to the, inherited, Parameters property. The first parameter is an integer named id and the second a string named name. The BaseConstructorArgs property is used to call the base class default constructor. To call the base class constructor with no arguments we have used an instance of the CodeSnippetExpression class with the value Empty. The same technique can be used with the ChainedConstructorArgs property to call a constructor overload with no arguments.

Both constructors are generated with public access by setting their Attributes property access flags to MemberAttributes.Public whilst masking out the access flags using the MemberAttributes.AccessMask.

The CodeConstructor class is used to create and generate an instance constructor for a type, to generate a static constructor we have to create an instance of the CodeTypeConstructor class and add it to the CodeTypeDeclaration instance's Members collection. A static constructor has a very simple signature with no parameters, base class or overload calls, so adding a static constructor to a type is very simple:

sampleClass.Members.Add(new CodeTypeConstructor());

The generated CSharp source code for the example class with a static constructor:

using System;

namespace Samples {

    public class Customer {

        static Customer() {
        }
    }
}

The generated Visual Basic source code for the example class with a static constructor:

Imports System

Public Class Customer

    Shared Sub New()
    End Sub
End Class

Note

When creating objects to add to the CodeDOM and when setting the properties of a CodeDOM object, typically, you have to make the choice of whether to create an object in one using the constructor or to create an instance and then set its properties. To a certain extent the choice boils down to personal preference, however, in a lot of cases using the constructor can lead to a very long line of code with a lot of nested brackets because, typically, one object is created using one or more other objects which also have to be constructed in the same line of code.

Until you become familiar with the classes in the System.CodeDom namespace we think that, where possible, it is easier to create an object using the default or the least complex constructor and then set its properties. Creating multiple objects within a single outer constructor call can be slightly bewildering when you are new to the CodeDOM.

Generating Properties

To generate a property we need to add an instance of the CodeMemberProperty class to the CodeTypeDeclaration instance's Members property. The CodeMemberProperty class only has a default, parameterless, constructor so we have to create an instance and then set its properties. We need to specify a name, a type and the property access and then specify the get and/or the set statements.

To keep the sample code short and to concentrate on the creation of a property we have omitted the code to create the namespace and class. The omitted code creates a CodeTypeDeclaration instance variable named "sampleClass".

ExpandSample code for generating a read only property:

The generated CSharp source code for the example class with a read only property:

using System;

namespace Samples {

    public class Customer {

        public int Id {
            get {
                throw new System.NotSupportedException();
            }
        }
    }
}

The generated Visual Basic source code for the example class with a read only property:

Imports System

Public Class Customer

    Public ReadOnly Property Id() As Integer
        Get
            Throw New System.NotSupportedException
        End Get
    End Property
End Class

The example generation code should be familiar down to the point where it creates the get statements. Up to now we have, mostly, been demonstrating the creation of types and member signatures, this is the first time that we have encountered the body or code statements of a member. Typically, when generating class templates, code statements are not included, the developer adds the implementation. However, if we generate a property get member or a non-void method (Function in Visual Basic) we have to include either a return statement or throw an exception, otherwise the generated source code will not compile.

Whilst demonstrating the creation and generation of types and their members we are, in effect, generating template code, we will discuss generating statements and expressions later in the article. We do, however, want the generated sample code to compile, so we will generate a throw exception statement for property getters and non-void methods.

We prefer to generate a NotSupportedException rather than a NotImplementedException because the NotSupportedException has a, system supplied, default error message and the NotImplementedException does not. Therefore, we do not have to create CodeExpression instances to provide constructor arguments when creating the exception, we can specify an empty array!

To create the exception throw statement for the property getter we use an instance of the CodeThrowExceptionStatement class. This class is a glowing example of the complexity of construction implemented by some of the System.CodeDom classes. The documentation for the CodeThrowExceptionStatement class constructor looks fairly benign, the constructor we require simply specifies an instance of the CodeExpression class as its only parameter. However, we need to specify an instance of the CodeObjectCreateExpression class to create and generate the throw statement and to construct an instance of this class we need to specify the type we want to create and an array of CodeExpression instances to specify the constructor arguments.

By default, properties are generated with private access, therefore, if we want a public property we have to set the MemberAttributes.Public access flag. To create and generate a read only property is very straight forward, if we don't assign a value to the SetStatements property we generate a read only property.

ExpandSample code for generating a read write property:

The generated CSharp source code for the example class with a read write property:

using System;

namespace Samples {

    public class Customer {

        public string Name {
            get {
                throw new System.NotSupportedException();
            }
            set {
                ;
            }
        }
    }
}

The generated Visual Basic source code for the example class with a read write property:

Imports System

Public Class Customer

    Public Property Name() As String
        Get
            Throw New System.NotSupportedException
        End Get
        Set

        End Set
    End Property
End Class

The example generation code creates a throw exception statement for the GetStatements property and then assigns an instance of the CodeSnippetExpression class, initialized to Empty, to the SetStatements property to create an empty setter template.

Typically, properties do not have parameters, however, the CodeMemberProperty class does have a Parameters property which references a collection of CodeParameterDeclarationExpression instances. The Parameters property is implemented to facilitate the creation and generation of an indexer property. To create an indexer, the CodeMemberProperty instance's, inherited, Name property must be set to the "special" value of "Item" and the Parameters property must contain one or more parameter references.

To keep the sample code short and to concentrate on the creation of an indexer property we have omitted the code to create the namespace and class. The omitted code creates a CodeTypeDeclaration instance that represents a collection class derived from Collection<T> and assigns the instance to a variable named "sampleClass".

ExpandSample code for generating an Item or indexer property:

The generated CSharp source code for the example class with an indexer property:

using System;
using System.Collections.ObjectModel;

namespace Samples {

    public class CustomerCollection : Collection<Customer> {

        public Customer this[string name] {
            get {
                throw new System.NotSupportedException();
            }
        }
    }
}

The generated Visual Basic source code for the example class with an indexer property:

Imports System
Imports System.Collections.ObjectModel

Public Class CustomerCollection
    Inherits Collection(Of Customer)

    Public Default ReadOnly Property Item(ByVal name As String) As Customer
        Get
            Throw New System.NotSupportedException
        End Get
    End Property
End Class

The example generation code creates a CodeMemberProperty instance, assigns the "special" name value "Item" to denote that the property is an indexer and then adds a CodeParameterDeclarationExpression instance to the Parameters collection to define a String parameter named "name". In this example the indexer is read only, however, we could have generated a setter by assigning a value to the SetStatements property.

A property may implicitly or explicitly implement an interface member and the CodeMemberProperty class has an ImplementationTypes property that references a collection of CodeTypeReference instances that represent the interface types, implicitly, implemented by the property and a PrivateImplementationType property that references a CodeTypeReference instance representing the interface type, explicitly, implemented by a private property.

Generating Events

To generate an event we need to add an instance of the CodeMemberEvent class to the CodeTypeDeclaration instance's Members property. The CodeMemberEvent class only has a default, parameterless, constructor so we have to create an instance and then set its properties. We need to specify an event name and an event type and preferably we should specify the generic System.EventHandler delegate as the type.

To keep the sample code short and to concentrate on the creation of an event we have omitted the code to create the namespace and class. The omitted code creates a CodeTypeDeclaration instance variable named "sampleClass".

ExpandSample code for generating an event:

The generated CSharp source code for the example class with an event:

using System;

namespace Samples {

    public class Customer {

        public event System.EventHandler<BalanceChangedEventArgs> BalanceChanged;
    }
}

The generated Visual Basic source code for the example class with an event:

Imports System

Public Class Customer

    Public Event BalanceChanged As System.EventHandler(Of BalanceChangedEventArgs)
End Class

The example generation code creates a CodeMemberEvent instance and assigns the name of the event as "BalanceChanged". The code then builds the type of the event by specifying the generic System.EventHandler with a generic type parameter value of "BalanceChangedEventArgs". To specify an event that does not pass event data to the handler use the EventArgs type as the generic type parameter or create the CodeTypeReference instance as follows:

eventType = new CodeTypeReference(typeof(EventHandler<EventArgs>));

By default, events are generated with private access, therefore, if we want a public event we have to set the MemberAttributes.Public access flag.

An event may implicitly or explicitly implement an interface member and the CodeMemberEvent class has an ImplementationTypes property that references a collection of CodeTypeReference instances that represent the interface types, implicitly, implemented by the event and a PrivateImplementationType property that references a CodeTypeReference instance representing the interface type, explicitly, implemented by a private event.

Generating Methods

To generate a method we need to add an instance of the CodeMemberMethod class to the CodeTypeDeclaration instance's Members property. The CodeMemberMethod class only has a default, parameterless, constructor so we have to create an instance and then set its properties. We need to specify a name, parameters, return type and the method access and then specify the statements.

To keep the sample code short and to concentrate on the creation of a method we have omitted the code to create the namespace and class. The omitted code creates a CodeTypeDeclaration instance variable named "sampleClass".

ExpandSample code for generating a void method:

The generated CSharp source code for the example class with a void method:

using System;

namespace Samples {

    public class Customer {

        public void UpdateCreditLimit(decimal limit) {
        }
    }
}

The generated Visual Basic source code for the example class with a void method:

Imports System

Public Class Customer

    Public Sub UpdateCreditLimit(ByVal limit As Decimal)
    End Sub
End Class
ExpandSample code for generating a non-void method:

The generated CSharp source code for the example class with a non-void method:

using System;

namespace Samples {

    public class Customer {

        public decimal AddToBalance(int id, decimal sale) {
            throw new System.NotSupportedException();
        }
    }
}

The generated Visual Basic source code for the example class with a non-void method:

Imports System

Public Class Customer

  Public Function AddToBalance(ByVal id As Integer, ByVal sale As Decimal) As Decimal
    Throw New System.NotSupportedException
  End Function
End Class

The example generation code creates a CodeMemberMethod instance and assigns the name of the method, the parameters and the return type. For void (Sub in Visual Basic) methods the System.Void type is specified as the return type.

By default, methods are generated with private access, therefore, if we want a public method we have to set the MemberAttributes.Public access flag.

A method may implicitly or explicitly implement an interface member and the CodeMemberMethod class has an ImplementationTypes property that references a collection of CodeTypeReference instances that represent the interface types, implicitly, implemented by the method and a PrivateImplementationType property that references a CodeTypeReference instance representing the interface type, explicitly, implemented by a private method.

The CodeMemberMethod class has a TypeParameters property that references a collection of CodeTypeParameter instances. The TypeParameters property can be used to specify the type parameters when generating a generic method.

Specifying the Access and Scope of a Type Member

The CodeMemberEvent, CodeMemberField, CodeMemberMethod and CodeMemberProperty classes are all derived from the CodeTypeMember base class and the CodeConstructor class derives, indirectly, from CodeTypeMember through its base class CodeMemberMethod. All of these classes inherit an Attributes property from CodeTypeMember which is typed as MemberAttributes. The MemberAttributes enumeration defines members to set member access, public, private, family, assembly, etc. and members to set member scope, abstract, final, static, override, etc.

The MemberAttributes enumeration is a bit of a weird beast, it looks like a bit field, it is used like a bit field but it is not decorated with the FlagsAttribute. However, we reckon if it looks like a duck, walks like a duck and quacks like a duck then the chances are, it is a duck!

When we create an instance of the one of the CodeMember* classes or the CodeConstructor class, the Attributes property is initialized to a default value of (Private | Final). Therefore, for example, creating and generating a method using the default Attributes value as follows:

CodeMemberMethod test = new CodeMemberMethod();
test.Name = "TestMethod";
test.ReturnType = new CodeTypeReference("System.Void");

produces the following generated method signature:

CSharp:

private void TestMethod() {
}

Visual Basic:

Private Sub TestMethod()
End Sub

If we, explicitly, set the access to Public we will clear the Final scope flag and generate a virtual (Overridable in Visual Basic) method as follows:

test.Attributes = MemberAttributes.Public;

produces the following generated method signature:

CSharp:

public virtual void TestMethod() {
}

Visual Basic:

Public Overridable Sub TestMethod()
End Sub

If, however, we want a public method but we don't want it to be virtual, we have to use the MemberAttributes.AccessMask enumerated value to mask out the access flag and leave the Final scope flag bit set, as follows:

test.Attributes =
          (test.Attributes & ~MemberAttributes.AccessMask) |
             MemberAttributes.Public;

produces the following generated method signature:

CSharp:

public void TestMethod() {
}

Visual Basic:

Public Sub TestMethod()
End Sub

Explicitly setting a scope flag also clears the other flags, so if we want to generate an override (Overrides in Visual Basic) method, for example, we have to use the MemberAttributes.ScopeMask enumerated value to mask out the scope flag, as follows:

test.Attributes =
          (test.Attributes & ~MemberAttributes.ScopeMask) |
             MemberAttributes.Override;

produces the following generated method signature:

CSharp:

private override void TestMethod() {
}

Visual Basic:

Private Overrides Sub TestMethod()
End Sub

However, we are unlikely to want a private override! Therefore to generate a protected override we are not bothered about clearing the default flags, so we can, explicitly, set the required flags, as follows:

test.Attributes = MemberAttributes.Family | MemberAttributes.Override;

produces the following generated method signature:

CSharp:

protected override void TestMethod() {
}

Visual Basic:

Protected Overrides Sub TestMethod()
End Sub

When setting an access flag, use the MemberAttributes.AccessMask enumerated value if you want to change only the access flag and leave the other flags set. When setting a scope flag, use the MemberAttributes.ScopeMask enumerated value if you want to change only the scope flag and leave other the flags set. If you are changing both the access and scope flags or if you specifically want to clear the, relevant, other flags you can set the relevant flags directly.

Generating Expressions and Statements

To generate the code body for a property getter, property setter or method we need use the classes that derive from the CodeExpression and CodeStatement base classes. Although, as stated earlier in this article, you will, typically, have to resort to literal code snippets when generating complex or compiler enhanced code constructs, you can generate most of the commonly used code constructs, in a language-independent way, with the CodeExpression and CodeStatement derived classes.

We don't have the time or the space to cover every single permutation of the expressions and statements that can be generated, so we will use a single method implementation to cover some of the common constructs to demonstrate the basic techniques of creating code bodies.

To keep the sample code short and to concentrate on the creation of a code body we have omitted the code to create the namespace and class. The omitted code creates a CodeTypeDeclaration instance variable named "sampleClass" and adds the following method template to it:

ExpandSample code for generating the method template:

The generated CSharp source code for the example method template:

public string CreateSalutation(string title) {
}

The generated Visual Basic source code for the example method template:

Public Function CreateSalutation(ByVal title As String) As String
End Function

The sample code will now concentrate on building the code body for the "test" method. We will build the source code in stages but we will show the full method when it is finished.

ExpandSample code for generating an if statement to test the method parameter for null:

The generated CSharp source code for the example if statement:

if ((title == null)) {
    throw new System.ArgumentNullException("title");
}

The generated Visual Basic source code for the example if statement:

If (title Is Nothing) Then
    Throw New System.ArgumentNullException("title")
End If

If you are thinking that is a lot of code to generate a simple if statement then you are starting to get the idea! The double brackets are an anomaly of the CSharp code provider, it seems to create a bracket pair for the CodeConditionStatement and then creates another pair for the CodeBinaryOperationExpression which looks odd for an if statement with a single condition expression. This is another case of generated code that is not "pretty" but is valid and compiles without error.

ExpandSample code for generating and initializing two local variables:

The generated CSharp source code for the example variable declarations:

string salutation = string.Empty;
string name = this.CreateFullName();

The generated Visual Basic source code for the example variable declarations:

Dim salutation As String = String.Empty
Dim name As String = Me.CreateFullName
ExpandSample code for generating an if statement that builds the return salutation value:

The generated CSharp source code for the second if statement:

if ((name.Length > 0)) {
    salutation = string.Concat(title, " ", name);
}

The generated Visual Basic source code for the second if statement:

If (name.Length > 0) Then
    salutation = String.Concat(title, " ", name)
End If
ExpandSample code for generating the method return statement:

The generated CSharp source code for the return statement:

return salutation;

The generated Visual Basic source code for the return statement:

Return salutation

We have now completed the method body and the generated source code for the complete method is as follows.

ExpandThe generated CSharp source code for the example method:
ExpandThe generated Visual Basic source code for the example method:

We won't show the code required to generate the method source code in full, as it may just put you off writing a code generator! However, if you review the code required to generate each of the individual statements you will appreciate that developing a code generator is not a trivial task and source code generation has to deliver sufficient benefits to make the effort worthwhile. Fortunately, if you pick the right sort of class or classes to generate, the time saving and code consistency provided by source code generation more than repay the initial development effort.

Generating Code and Documentation Comments

Documentation comments can be added to any CodeTypeMember derived class using the Comments property which references a collection of CodeCommentStatement instances. Some of the other System.CodeDom classes also implement a Comments property that may or may not work in the same way as the CodeTypeMember Comments property. For example, interestingly, the CodeNamespace class has a Comments property, however, you should not add documentation comments to a CodeNamespace because most language compilers will not generate documentation for a namespace. You should only add standard code comments to a CodeNamespace instance using its Comments property, if you add documentation comments, most code providers that support documentation comments seem happy to output namespace documentation comments but they won't be processed by the relevant compiler (for now?).

Comments, standard or documentation, that you want to appear above a type or member should be added using the member's, inherited, Comments property. Standard code comments that you want to appear in a member's code body should be added to the member's CodeStatementCollection. For example, to add code comments to a property getter, use the GetStatements property, to add code comments to a method use the Statements property.

Code comments are created using an instance of the CodeComment class, the DocComment property should be set to true, either explicitly or during construction, to specify that the comment is a documentation comment, by default the DocComment property is set to false. A CodeComment instance cannot be directly assigned to a code object, it has to be assigned to a CodeCommentStatement class instance before assigning the CodeCommentStatement instance to the code object. You can create an instance of the CodeCommentStatement class directly, to short-cut the creation process or you can first create the CodeComment instance and then use it to construct an instance of the CodeCommentStatement class. The CodeCommentStatement class represents a single line comment, to create multi-line comments you have to use multiple instances of the CodeCommentStatement class, one instance per line.

ExpandSample code for creating CodeCommentStatement instances:
ExpandSample code for creating standard and documentation comments:

The generated CSharp source code for the standard and documentation comments example:

using System;

// CodeDom samples.
namespace Samples {

    /// <summary>
    /// Type documentation summary comment.
    /// </summary>
    public class TestClass {

        /// <summary>
        /// Type member documentation summary comment.
        /// </summary>
        public void TestMethod(int id) {
            // TODO: Add method implementation.
        }
    }
}

The generated Visual Basic source code for the standard and documentation comments example:

Imports System

'''<summary>
'''Type documentation summary comment.
'''</summary>
Public Class TestClass

    '''<summary>
    '''Type member documentation summary comment.
    '''</summary>
    Public Sub TestMethod(ByVal id As Integer)
        'TODO: Add method implementation.
    End Sub
End Class

Generating the Source Code

In the preceding sections we have discussed code providers and demonstrated how to use the types in the System.CodeDom namespace to create a Code Document Object Model and we have shown the source code generated from the examples. To keep the examples short and to the point we have not, yet, demonstrated how to use a code provider to generate the source code. For all of the examples in this article we used the following, helper, methods to generate the source code.

ExpandUsing a code provider to generate .Net source code:

The example code uses the code provider's static CreateProvider method to create an instance of a CodeProvider derived language-specific code provider. The source code output file path is built using a constant to specify the folder path and file base name and the provider's FileExtension property is used to append the language-specific source code file extension e.g. "cs", "vb", etc. to the file base name. In a "real" code generator application the user would, typically, specify the path and name of the source code file but you should still use the FileExtension property to append the relevant file extension.

For the examples demonstrated in this article we have use the default generation options as defined in an instance of the System.CodeDom.Compiler.CodeGeneratorOptions class. You can use the CodeGeneratorOptions class to specify how the source code should be formatted, for example, you can specify bracing using the BracingStyle property, the code indent using the IndentString property and the output order using the VerbatimOrder property. If you specify formatting options you should note that some development environments allow a developer to specify their own, preferred, formatting options. For example, Visual Studio allows a developer to specify bracing style and the environment setting will override the formatting specified when generating the source code.

The System.CodeDom.Compiler.IndentedTextWriter class is used to write the source code out to file. This is good choice for source code output but any System.IO.TextWriter derived class can be used.

Finally, the code provider's GenerateCodeFromCompileUnit method is used to generate the source code from the source code object model and write the code out to a file using the specified generator options.

The CodeDomProvider base class defines several methods that can be overridden by a language-specific code provider to generate source code, you don't have to use a CodeCompileUnit. For example, you can use the GenerateCodeFromMember method to generate source code from a CodeTypeMember derived class or the GenerateCodeFromStatement method to generate source code from a CodeStatement derived class.

Conclusion

We have covered a lot of ground in this article but inevitably, we have had to miss out quite a lot as well, we do, however, hope that we have whetted your appetite for source code generation and that you will start developing your own code generators.

Is source code generation worth the effort? We think that it most definitely is. Pick your subjects carefully and concentrate on automating the generation of boilerplate code i.e. code that you and your team use repeatedly, code that is similar in each implementation but not identical and you will reap the rewards i.e. save development and testing time and produce more consistent code.