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
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:
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.
namespace MyNamespace
{
public void MyClass
{
}
}
namespace MyNamespace2
{
using MyAlias = MyNamespace.MyClass;
public void MyClass2 : MyAlias
{
}
}
namespace OutterNamespace
{
namespace InnerNamespace
{
public void MyClass
{
}
}
}
using System;
using System.CodeDom;
using System.CodeDom.Compiler;namespace MyNamespace
{
public void MyClass
{
public void MyClass()
{
int i,j,k;
bool blnYes, blnNo;
}
}
}
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 |
FieldDirection |
Specifies |
MemberAttributes |
Specifies |
|
|
System.CodeDom Classes |
|
CodeArgumentReferenceExpression |
Represents |
CodeArrayCreateExpression |
Represents |
CodeArrayIndexerExpression |
Represents an expression |
CodeAssignStatement |
Represents |
CodeAttachEventStatement |
Represents a statement |
CodeAttributeArgument |
Represents |
CodeAttributeArgumentCollection |
Represents |
CodeAttributeDeclaration |
Represents |
CodeAttributeDeclarationCollection |
Represents |
CodeBaseReferenceExpression |
Represents |
CodeBinaryOperatorExpression |
Represents |
CodeCastExpression |
Represents |
CodeCatchClause |
Represents |
CodeCatchClauseCollection |
Represents |
CodeComment |
Represents |
CodeCommentStatement |
Represents |
CodeCommentStatementCollection |
Represents a collection |
CodeCompileUnit |
Provides a top-level |
CodeConditionStatement |
Represents |
CodeConstructor |
Represents |
CodeDelegateCreateExpression |
Represents |
CodeDelegateInvokeExpression |
Represents an expression |
CodeDirectionExpression |
Represents an expression |
CodeEntryPointMethod |
Represents |
CodeEventReferenceExpression |
Represents an expression |
CodeExpression |
Represents a code |
CodeExpressionCollection |
Represents a collection |
CodeExpressionStatement |
Represents |
CodeFieldReferenceExpression |
Represents a reference to |
CodeGotoStatement |
Represents a goto |
CodeIndexerExpression |
Represents a reference to |
CodeIterationStatement |
Represents a for |
CodeLabeledStatement |
Represents a labeled |
CodeLinePragma |
Represents a specific |
CodeMemberEvent |
Represents an event |
CodeMemberField |
Represents a field class |
CodeMemberMethod |
Represents a declaration |
CodeMemberProperty |
Represents |
CodeMethodInvokeExpression |
Represents |
CodeMethodReferenceExpression |
Represents |
CodeMethodReturnStatement |
Represents a return |
CodeNamespace |
Represents a namespace |
CodeNamespaceCollection |
Represents a collection |
CodeNamespaceImport |
Represents |
CodeNamesapceImportCollection |
Represents a collection |
CodeObject |
Provides a common base |
CodeObjectCreateExpression |
Represents an expression |
CodeParamaterDeclarationExpression |
Represents a parameter |
CodeParameterDeclarationExpressionCollection |
Represents a collection |
CodePrimitiveExpression |
Represents a primitive |
CodePropertyReferenceExpression |
Represents |
CodePropertySetValueReferenceExpression |
Represents an expression |
CodeRemoveEventStatement |
Represents |
CodeSnippetCompileUnit |
Represents a literal code |
CodeSnippetExpression |
Represents a literal |
CodeSnippetStatement |
Represents a statement |
CodeSnippetTypeMember |
Represents a member of a |
CodeStatementCollection |
Represents a collection |
CodeThisReferenceExpression |
Represents a reference to |
CodeThrowExceptionStatement |
Represents |
CodeTryCatchFinallyStatement |
Represents a try block, |
CodeTypeConstructor |
Represents a static |
CodeTypeDeclaration |
Represents a type |
CodeTypeDeclarationCollection |
Represents a collection |
CodeTypeDelegate |
Represents a delegate |
CodeTypeMember |
Represents the |
CodeTypeMemberCollection |
Represents a collection |
CodeTypeOfExpression |
Represents a typeof |
CodeTypeReference |
Represents a data type to |
CodeTypeReferenceCollection |
Represents a collection |
CodeTypeReferenceExpression |
Represents a reference to |
CodeVariableDeclarationStatement |
Represents a declaration |
CodeVariableReferenceExpression |
Represents an expression |
System.CodeDom.Compiler Namespace Overview
System.CodeDom.Compiler Enumerations GeneratorSupport Specifies LanguageOptions Specifies System.CodeDom.Compiler Interfaces ICodeCompiler Provides ICodeGenerator Provides ICodeParser Specifies an interface System.CodeDom.Compiler CodeCompiler Provides CodeDomProvider Provides CodeGenerator Provides CodeGeneratorOptions Represents CodeParser Provides CompilerError Represents CompilerErrorCollection Represents CompilerParameters Represents CompilerResults Represents Executor Provides IndentedTextWriter Provides TempFileCollection Represents a collection
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.
identifiers used to determine whether a code generator supports certain types
of code.
identifiers that indicate special features of a language.
a compiler execution interface.
an interface for generating code.
for parsing code into a CodeCompileUnit.
Classes
a helper class for implementing an ICodeCompiler.
a base class for CodeDomProvider implementations. This class is
abstract.
a helper class for implementing an ICodeGenerator.
This class is abstract.
options used by code generators.
a helper class for implementing an ICodeParser.
a compiler error or warning.
a collection of CompilerError
objects.
the parameters used to invoke the compiler.
the results of compilation that are returned from the compiler.
command execution functions for invoking compilers. This class cannot be
inherited.
a text writer that can indent new lines by a tab string token.
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: ///
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
The CodeDom provides the following advantages: Other related CodeDom Technologies include: Reflection, Emitting and Custom Attributes.
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 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.
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.