Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Using CodeDOM Code Generation to Implement Repetitive Patterns : Page 4

Implementing simple patterns is often boring, time-consuming, and error-prone—just because they're so simple. But by using code generation you can prevent a lot of the drudgery.

Step 2: Enumerating Types
You may remember from earlier in the article that you need a type-discriminator so that the data layer knows which types to instantiate. Fortunately, building an enumeration type is the easiest possible non-trivial CodeDOM operation—because really, an Enum is just a class with only data-members. You'll start to notice a theme when working with the CodeDOM namespace: structure is easier than content. And enums are all structure.

The snippet below includes all the code you need to build an enum: just loop through all of the types, and add a member for each one.

TypeCollection allTypes = inheritanceTree.GetAllTypes(); _typeEnum = new CodeTypeDeclaration( "TypeEnum" ); _typeEnum.IsEnum=true; for(int i = 0; i<allTypes.Count; i++) { Type t = allTypes[i]; CodeMemberField field = new CodeMemberField(); field.Name=t.Name; field.InitExpression= new CodePrimitiveExpression(i); _typeEnum.Members.Add(field); }

Step 3: Construction
Next, you have to write the code that outputs constructors. The default constructor is easy enough to build:

private CodeConstructor BuildDefaultCtor() { CodeConstructor defaultConstructor = new CodeConstructor(); defaultConstructor.Attributes = MemberAttributes.Private; return defaultConstructor; }

And here's the output:

private ContactPropertyUnion() { }

A non-trivial constructor takes a little more effort. The example below takes a reference to a wrappable object, and assigns it to a data member. Remember our theme: In CodeDOM, structure is easier than content. Now, we're getting into some content. This method you're trying to output has just one line of code, but writing it with CodeDom takes a dozen:

private CodeConstructor BuildWrapperCtor() { CodeConstructor wrapCtor = new CodeConstructor(); wrapCtor.Attributes=defaultVisibility; //Setup the single parameter. CodeParameterDeclarationExpression toWrapParam = new CodeParameterDeclarationExpression(); toWrapParam.Name="toWrap"; toWrapParam.Type = new CodeTypeReference( inheritanceTree.Root ); wrapCtor.Parameters.Add(toWrapParam); //Assign the parameter to the data member. CodeAssignStatement assign = new CodeAssignStatement(); assign.Left = WrappedFieldReference; assign.Right = new CodeVariableReferenceExpression( toWrapParam.Name); wrapCtor.Statements.Add(assign); return wrapCtor; }

Here's the output:

internal ContactPropertyUnion(Contact toWrap) { wrapped=toWrap; }

Step 4: Properties
Finally, you're getting to the heart building PropertyUnion classes automatically. Essentially, every property getter has four steps:

  1. Initialize a default variable. (Just in case the wrapped object doesn't implement the property.)
  2. Check the type of the wrapped object.
  3. Depending on the type, cast the wrapped object to its concrete type and invoke the appropriate property on the cast object.
  4. Return the resulting value.
That's not too bad. These five steps contain mostly content—such as conditionals and property or method invocations. Listing 2 builds an accessor based on a PropertyInfo object from the System.Reflection namespace. It's long, but at least you only have to write it once.

So that's the high-level CodeDOM picture. Of course there are some details, things like comments, and building defaults, so take a look at the sample application for details. But most of the work takes place in the snippets above. Even more than elsewhere, keeping things simple really pays off when working with code generation.

I'm not going to tell you that the PropertyUnion is the greatest design pattern since the Singleton. It's not. But it can be a good way to centralize type-checking-ugliness in a single class, especially when you're doing ugly type-checking in different layers of your application.

The real point is this: If you can reduce a pattern down to its bare, mindless essentials, if you can simplify it until it becomes simplistic, then you can probably build a reusable implementation with code generation.

Eric McMullen is a director at Falstaff Solutions, a Denver-based consulting house which specializes in data-centric .NET applications. Check out Falstaff's Web site at www.falstaffsolutions.com.
Comment and Contribute








