February 24, 2021
Hot Topics:

Creating Simplified Code Generators in VS.NET, Part II

  • By Paul Kimmel
  • Send Email »
  • More Articles »

Code generators are powerful and contribute in diverse circumstances in .NET. If you have used a Web Service then you benefited from the code generator relying on the CodeDOM that writes a proxy class, making the Web Service easier to call. (It is also easy to overlook the existence of the proxy class.) This is what good technology should do for us; it should solve problems without being obtrusive.

In the second half of this two part article I will demonstrate how to use the powerful capabilities of .NET to incorporate simple code generators into VS.NET, helping you and your co-workers become more productive. The example in this section actually uses macros and the Automation extensibility model. For more advanced code generators you have the option of using the Reflection.Emit namespace or System.CodeDOM. The macro example uses string substitution. The Reflection.Emit namespace emits CIL (Common Intermediate Language), and the CodeDOM can generate C#, VB .NET, and J# .NET source code.

Let's set the stage for the task we are trying to automate and proceed.

Defining the Code Template

Working on a big project recently we had tens of thousands of lines of code. A significant portion of that code contained properties that modified underlying fields, as many classes will. I got tired of writing the property statement from scratch dozens of times per day. To eliminate some of the tedium of writing property methods, I elected to show VS.NET how to do this work for me. As you will see the task was not completely automated, but a significant portion of the typing was handled by VS.NET. (If you are jumping in the middle of this article, keep in mind that the benefit of writing code generators is to aid productivity through the speed and accuracy of generated code.)

The first step in using a macro to generate code is to open the Macros IDE, add a new module, a macro, and stub out the code template. Here are the steps stubbing a new macro and the syntactical template for a property is shown in listing 1 and the templatized version is shown in listing 2.

  1. To create a new macro, open Visual Studio .NET—I am using VS.NET 2003, but the example works in version 1—and select Tools|Macros|Macros IDE
  2. In the Macros Project Explorer click on the MyMacros project, right-clicking Add|Add Module from the Project Explorer context menu
  3. Add a public subroutine named WriteProperty to the module

After step 3 we are ready to stub out the code template. Listing 1 contains a syntactical example of the property we want to write and listing 2 converts that example to a template.

Listing 1: The syntax of a field and its associated property.

Private FField As String

Public Property Field() As String
  Return FField
End Get
Set(ByVal Value As String)
  If (FField = Value) Then Return
  FField = Value
End Set
End Property

Listing 2: A templatized version of a property.

Private Cr As String = Environment.NewLine

Private mask As String = _
  "Public Property {0}() As {1}" + Cr + _
  "  Get" + Cr + _
  "    Return {2}" + Cr + _
  "  End Get" + Cr + _
  "  Set(ByVal Value As {1})" + Cr + _
  "    If ({2} = Value) Then Return" + Cr + _
  "    {2} = Value" + Cr + _
  "  End Set" + Cr + _
  "End Property" + Cr

The code in listing 2 defines a field named mask that contains a parameterized version of a property. Pagination is used literally to manage layout and parameters are used to represent aspects of the template that will be replaced by literal values. The parameter {0} represents the Property name. {1} represents the Property type, and {2} will be replaced with the underlying field value.

The next step is to write some code that substitutes literal values for the parameters in the mask string. We can accomplish this step by using the plain old vanilla InputBox function. We need to prompt for the property name and data type and the field name.

Rather than attempting to get all of the code right all at once, we can stage our solution, testing incremental revisions as we proceed. To that end we can add the queries to obtain the parameterized values and send the results to the Output window to ensure that the code is generated correctly. The staged revisions are shown in listing 3.

Listing 3: Incrementally testing additions to the code generator.

Imports EnvDTE
Imports System.Diagnostics
Imports System

Public Module Generator

  Private Cr As String = Environment.NewLine

  Private mask As String = _
    "Public Property {0}() As {1}" + Cr + _
    "  Get" + Cr + _
    "    Return {2}" + Cr + _
    "  End Get" + Cr + _
    "  Set(ByVal Value As {1})" + Cr + _
    "    If ({2} = Value) Then Return" + Cr + _
    "    {2} = Value" + Cr + _
    "  End Set" + Cr + _
    "End Property" + Cr

  Public Sub WriteProperty()
    Dim PropertyName As String = _
      InputBox("Property name:", "Property Name")

    Dim PropertyType As String = _
      InputBox("Property type:", "Property Type")

    Dim FieldName As String = _
      InputBox("Field name:", "Field Name")

    Dim Code As String = String.Format(mask, _
      PropertyName, PropertyType, FieldName)

End Sub

Run the macro in the Macros IDE by placing the cursor anywhere in the WriteProperty subroutine and pressing F5. The code displays three InputBoxes and then substitutes the parameterized values in mask with the input values, writing the results to the Output window (shown in figure 1).

Figure 1: The generate code written to the Output window.

The code generates correctly, as depicted in the figure. The next step is to send the generated code to a VB .NET source code file.

Page 1 of 2

This article was originally published on May 28, 2003

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date