Write Macro Code Generators with VS 2005

In his Dr. Dobb's Journal review of 0972652914, The Nature of Order, the four-volume series by Christopher Alexander, Jacek Sokulski writes "generative processes always operate on the whole, such as in embryo development where the system is not composed from parts, but the whole unfolds from the very beginning. This unfolding is through a sequence of transformations."

Sokulski mentions that at present software is not automatically generative, but we have been told that software should evolve over generations. So why aren't we working on automatically generative software? Why aren't we working on software that grows or evolves automatically over generations of use as opposed to generations of changes? Which technologies do we have at present that support generative software, and which technologies are missing? The answers may not yet exist for those questions, but some capabilities in VS 2005 support generative code.

You can employ macros to write code for you. These macro code generators do not have to be random, and you don't have to rely on imagination or invention to figure out which kind of macros to write. Two independent but related fields of study—design patterns and refactoring—clearly offer a plethora of options for writing understood, well-documented code generators. (Code generation is not the same thing as generative code, but it is a cog in that engine.) This article demonstrates how to use the macro engine in VS 2005 to write a code generator that implements the refactoring Encapsulate Field for VB.NET.

Refactoring: Encapsulate Field

Refactoring is a defined process for improving the implementation of code. You achieve it by converting random constructs and idioms into a prescribed, predictably reliable use of constructs and idioms. In the simplest sense, refactoring takes some of the subjectivity out of code. As software engineers, we no longer have to depend on argument and force of will to determine whether code is good or not; we can employ an objective standard and agree that refactored code is preferable to code that is not.

Like design patterns, refactorings are named idioms that come replete with descriptions, instructions, and intended results. Any programmer, regardless of experience, can read the description, apply the ordered instructions much like following a recipe, and get a predictable improvement.

An example of a refactoring is called Encapsulate Field. Encapsulate Field is basically the name for making fields private and limiting access to those fields through public property methods. Limited access to an object's state is preferable to unlimited access, and Encapsulate Field is a belief in the value of constrained access to data. (Some people may not agree with the basic premise—constrained access is preferable to unfettered access—but some people don't believe objects are good. Rather than debate whether refactoring is good or bad, this article assumes that refactoring is good.)

Implement the Macro

When you are in Visual Studio 2005 in the context of a C# project, a refactoring menu is present. In a VB.NET project, no such menu is currently present (at least by the time beta 2 was released). However, you can easily emulate this supported behavior for VB.NET by writing a lightweight code generator to implement Encapsulate Field (or other refactorings).

To implement Encapsulate Field, automate the following steps:

  • Select a field without a coincidental property method.
  • Change the field's access modifier to private.
  • Change the field name slightly to avoid a property collision (using any convention you want).
  • Generate the getter and setter property methods and lines of code to provide access to that field.
Tip: A good way to begin working with macros is to turn on the macro recorder, complete a task, and see which macro statements the IDE writes. Next, generalize the recorded macro.

The Visual Studio object model supports all of these capabilities and significantly more. Listing 1 demonstrates an implementation of Encapsulate Field.

Listing 1: Encapsulate Field Written as a Code Generator Using the Macros Capability of VS 2005

Imports EnvDTE
Imports EnvDTE80
Imports System.Diagnostics

Public Class Refactoring

   Public Shared Sub EncapsulateField()
      Dim projectItem As ProjectItem = DTE.ActiveDocument.ProjectItem
      Dim fileCodeModel As FileCodeModel = projectItem.FileCodeModel

      ' Get the current selection
      Dim selection As TextSelection = DTE.ActiveDocument.Selection

      ' Get the current cursor location
      Dim point As TextPoint = selection.ActivePoint

      ' Try to read the current location as a code element
      Dim codeElement As CodeElement = _
          fileCodeModel.CodeElementFromPoint( _
          point, vsCMElement.vsCMElementVariable)

      If (codeElement Is Nothing) Then
         MsgBox("Place mouse cursor on field before running _
            this macro.", _
            MsgBoxStyle.Exclamation)
         Return
      End If

      Debug.Assert(codeElement.Kind = vsCMElement.vsCMElementVariable)

      ' we've tested so we know its a variable
      Dim codeVariable As CodeVariable = CType(codeElement, CodeVariable)
      Dim fieldName As String = codeVariable.Name
      Dim fieldType As String = codeVariable.Type.AsString

      ' rename the field so we don't have a collision with property
      codeVariable.Name = "F" & fieldName

      ' make sure field is private
      codeVariable.Access = vsCMAccess.vsCMAccessPrivate

      ' get the variable's parent
      Dim codeClass As CodeClass = CType(codeVariable.Parent, CodeClass)

      ' add a new property
      Dim codeProperty As CodeProperty = codeClass.AddProperty("dummy", _
          "dummy", fieldType, codeElement)

      codeProperty.Name = fieldName

      ' implement the getter
      Dim getter As EditPoint = codeProperty.Getter.GetStartPoint( _
         vsCMPart.vsCMPartBody).CreateEditPoint
      getter.LineDown()
      getter.Indent(, 3)
      getter.Insert("Return " + codeVariable.Name)
      ' implement the setter
      Dim setter As EditPoint = codeProperty.Setter.GetStartPoint( _
         vsCMPart.vsCMPartBody).CreateEditPoint
      setter.LineDown()
      setter.Indent(, 3)
      setter.Insert(codeVariable.Name + " = Value")
   End Sub
End Class

To try this sample, open the Macros IDE from the Tools|Macros menu in VS 2005, and create a new module under the macro file MyMacros (accessible from the Macros IDE's Project Explorer).

The object model for the VS IDE is huge, so I won't try to describe it all here. In addition, the code in Listing 1 is sufficiently commented such that you can readily determine what each chunk of macro code does. Essentially, given the following field:

Public Foo as Integer

The macro adds an F-prefix to the field name (a habit the author picked up from writing tons of Pascal code) and changes the field's access modifier to Private. Finally, the complete property statement is generated and added to the module containing field:

Private FFoo As Integer
Public Property Foo As Integer
Get
   Return FFoo
End Get
Set(ByVal value As Integer)
   FFoo = value
End Set
End Property

Experiment with the macro code by stepping through it in the Macros IDE.

Write Macro Code Generators with VS 2005

Test the Macro

The easiest way to test a macro is by running it in the Macros IDE. An easy way to test the code in context is to switch to the VS 2005 IDE, open the Command Window (View|Other Windows|Command Window), and type the entire macro subroutine name, including namespaces, at the Command Window prompt. Based on my implementation, you would select any field and enter the following in the IDE's Command window:

Macros.MyMacros.Refactoring.EncapsulateField

The Intellisense feature in the IDE will help you find the path to the macro if your path differs from mine.

Add the Macro to the Menubar

Having tested the refactoring Encapsulate Field, you can add it to the Visual Studio menubar or toolbar. To add access to the macro on the Tools menu, follow these steps:

  • In Visual Studio 2005, select Tools|Customize.
  • Pick the Command tab.
  • Select Macros from the Categories list and the macro you just created from the Commands list.
  • Drag and drop the macro on to the Tools menu.
  • Right-click on the command now residing on the Tools menu, use the context menu to change the menu name—to something like "Encapsulate Field", and add an icon if you want (see Figure 1).
  • Close the Customize dialog.

[MacroCodeGenerator.JPG]

Figure 1: Drag the Macro to the Menubar Where You'd Like It to Be Accessible

That's all there is to it. Your custom refactoring code generator is now an integrated part of your copy of the IDE. If you want to amaze your friends, locate the file mymacros.vsmacros (or export the module containing the refactoring) and e-mail it to them, along with the instructions for integrating the macro into the IDE. When they tell you how clever you are and you bask in the glory for a while, just tell them where you got the idea.

Ready for a Leap Forward

This article spotlighted an underutilized but powerful aspect of Visual Studio: macros and code generators. I really hope that we are on the cusp of more powerful and more interesting software systems. I still have at least 25 productive years left in me and hope to be around to see software that becomes truly generative and more interesting as a result.

Afterword

Without question, the accidental father of modern software design is Christopher Alexander. Oddly enough, Mr. Alexander writes about conventional architecture—buildings. The reason he is the father of modern design is because he writes about architecture from the basis of architectural patterns, and his ideas were openly adopted for modern software design. (Just look in the back of any book on design patterns and the like and you will see a bibliographical reference to A Pattern Language: Towns, Buildings, Construction. All of the comparisons to home building on software books is no accident.) Although I haven't met Mr. Alexander (but do have at least one copy of his book), it appears that he is with us and still writing. Presently, he is writing about the generative nature of things. I am curious to see what impact this will have on our industry.

About the Author

Paul Kimmel has written several books on object-oriented programming and .NET. Check out his upcoming book UML DeMystified from McGraw-Hill/Osborne (Spring 2005). Paul is also the founder and chief architect for Software Conceptions, Inc., founded 1990. He is available to help design and build software worldwide. You may contact him for consulting opportunities or technology questions at pkimmel@softconcepts.com.

If you are interested in joining, sponsoring a meeting, or posting a job, check out www.glugnet.org, the Web page of the Greater Lansing area Users Group for .NET.



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Learn How A Global Entertainment Company Saw a 448% ROI Every business today uses software to manage systems, deliver products, and empower employees to do their jobs. But software inevitably breaks, and when it does, businesses lose money -- in the form of dissatisfied customers, missed SLAs or lost productivity. PagerDuty, an operations performance platform, solves this problem by helping operations engineers and developers more effectively manage and resolve incidents across a company's global operations. …

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

Most Popular Programming Stories

More for Developers

RSS Feeds