Emitting Attributes by Using the CodeDom Classes

Emitting Attributes by Using the CodeDom Classes

Emitting IL is a low-level way to use Reflection to emit assemblies. You can emit a .NET language ”instead of IL ”by using the classes in the System.CodeDom namespace. CodeDOM can be used to emit and compile code ( System.CodeDom.Compiler ); applications that write .NET code using CodeDOM are referred to as code generators .

Listing 5.12 demonstrates how to create a code generator that generates VB .NET code and compiles that code to an executable assembly. The example uses CodeDOM to generate Visual Basic .NET code that produces an assembly equivalent to the emitted IL assembly from Listing 5.11.

Listing 5.12 Creating a Code Generator
 1:  Option Strict On 2:  Option Explicit On 3: 4:  Imports System.CodeDom 5:  Imports System.CodeDom.Compiler 6:  Imports System.Reflection 7:  Imports System.IO 8:  Imports System.Text.RegularExpressions 9: 10: Module CodeDOMAttributeDemo 11: 12:   Sub Main() 13:     Generator.Run() 14:   End Sub 15: 16: End Module 17: 18: Public Class Generator 19: 20:   Private CommentString As String = _ 21:     " Hello.vb - This code was generated by the CodeDOM.\r\n" + _ 22:     " Copyright (c) 2002. All Rights Reserved.\r\n" + _ 23:     " Written by Paul Kimmel. pkimmel@softconcepts.com\r\n" 24: 25:   Public Shared Sub Run() 26:     Dim G As Generator = New Generator() 27:     G.Generate() 28:   End Sub 29: 30:   Public Sub Generate() 31:     Dim CodeGenerator As ICodeGenerator = _ 32:       New VBCodeProvider().CreateGenerator 33:     Dim FileName As String = "Hello.vb" 34: 35:     Dim Stream As StreamWriter = New StreamWriter( _ 36:       New FileStream(FileName, FileMode.Create)) 37:     Try 38:       Dim Comment As CodeCommentStatement = _ 39:         New CodeCommentStatement(Regex.Unescape(CommentString)) 40: 41:       CodeGenerator.GenerateCodeFromStatement( _ 42:         Comment, Stream, Nothing) 43: 44:       Dim CompileUnit As CodeCompileUnit = _ 45:         New CodeCompileUnit() 46: 47:       CompileUnit.AssemblyCustomAttributes.Add( _ 48:         New CodeAttributeDeclaration("AssemblyDescriptionAttribute", _ 49:           New CodeAttributeArgument( _ 50:             New CodePrimitiveExpression("Code generated by CodeDOM")))) 51: 52:       Dim [Namespace] As CodeNamespace = _ 53:         New CodeNamespace("HelloWorld") 54: 55:       [Namespace].Imports.Add( _ 56:         New CodeNamespaceImport("System")) 57: 58:       CompileUnit.Namespaces.Add([Namespace]) 59: 60:       Dim TypeDeclaration As CodeTypeDeclaration = _ 61:         New CodeTypeDeclaration("Hello") 62: 63:       TypeDeclaration.IsClass = True 64: 65:       [Namespace].Types.Add(TypeDeclaration) 66: 67:       TypeDeclaration.TypeAttributes = TypeAttributes.Public 68: 69:       Dim MemberMethod As CodeEntryPointMethod = _ 70:         New CodeEntryPointMethod() 71: 72:       MemberMethod.Name = "Main" 73:       MemberMethod.ReturnType = Nothing 74: 75:       MemberMethod.Attributes = _ 76:         MemberAttributes.Public Or MemberAttributes.Static 77: 78:       MemberMethod.Statements.Add( _ 79:         New CodeMethodInvokeExpression( _ 80:           Nothing, "Console.WriteLine", _ 81:           New CodeArgumentReferenceExpression() _ 82:           {New CodeArgumentReferenceExpression( _ 83:             Chr(34) + "Hello, World!" + Chr(34))})) 84: 85:       TypeDeclaration.Members.Add(MemberMethod) 86: 87:       CodeGenerator.GenerateCodeFromCompileUnit( _ 88:         CompileUnit, Stream, Nothing) 89: 90:     Finally 91:       Stream.Close() 92:     End Try 93: 94:     Dim Compiler As ICodeCompiler = _ 95:       New VBCodeProvider().CreateCompiler 96: 97:     Dim CompilerParameters As CompilerParameters = _ 98:       New CompilerParameters() 99: 100:    CompilerParameters.GenerateExecutable = True 101:    CompilerParameters.MainClass = "HelloWorld.Hello" 102:    CompilerParameters.OutputAssembly = "Hello.exe" 103: 104:    Dim CompilerResults As CompilerResults = _ 105:      Compiler.CompileAssemblyFromFile( _ 106:      CompilerParameters, "Hello.vb") 107: 108:  End Sub 109: End Class 

The code generator defined in Listing 5.12 implements a simple Hello, World! application named Hello.exe . Like emitting IL, the code generator creates an executable assembly. (You can create DLLs or generate the code only.)

When you implement a code generator, you need to write code to create all the elements you would add if you were writing the code directly. The difference is that you must do this in the context of the classes in the System.CodeDom namespace. If you follow the code in lines 30 through 108 ”the Generate method ”you can discern the elements we are generating by class name.

Lines 31 and 32 demonstrate how to declare CodeGenerator as an interface and request a specific generator. In line 32 we create a new VBCodeProvider and use that class to create the code generator. If we swapped CSharpCodeProvider for VBCodeProvider and left all the other code the same, the code generator would generate C# code.

We want to generate code and save it to a file; we can use StreamWriter and FileStream as demonstrated in lines 35 and 36. The code is written to a file named Hello.vb in lines 87 and 88. Lines 38 through 42 demonstrate how to create an object that represents a comment and writes that comment to the stream containing the generated code.

Lines 47 through 50 demonstrate how to add attributes to the method. We add the AssemblyDescriptionAttribute , representing an assembly-level attribute that describes the assembly.

If you follow the rest of the code, you can see the elements you would expect in a VB .NET application. CodeCompileUnit in line 44 represents a unit. CodeNamespace in line 52 represents a namespace, to which we can add Imports statements, as demonstrated in lines 55 and 56. Line 58 demonstrates how to add a type to the namespace, including adding the Public access modifier in line 67. Lines 69 through 85 demonstrate adding a method to the new type. Generally we would add a CodeMemberMethod , but our console application needs an entry point. We use the CodeEntryPointMethod to indicate that Main will be our entry point for the console application.

Generating lines of code yields the most monolithic part of the generator. A call to Console.WriteLine("Hello, World!") requires all the code on lines 78 through 83. Basically, every element of the code statement has a constituent class in the CodeDom namespace. For example, the CodeArgumentReferenceExpression class is used to create an expression that is passed to CodeMethodInvokeExpression .

Lines 87 and 88 write the code to the Hello.vb file. Line 91 closes the file stream, and lines 94 through 106 create an instance of the VB .NET compiler and compile the Hello.vb module to Hello.exe .



Visual Basic. NET Power Coding
Visual Basic(R) .NET Power Coding
ISBN: 0672324075
EAN: 2147483647
Year: 2005
Pages: 215
Authors: Paul Kimmel

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net