Part 2: Automating the PropertyUnion Implementation
Writing the simple ContactPropertyUnion class by hand worked out great, but there are a couple of lingering maintainability problems:
- Any changes to the inheritance hierarchy require equivalent changes in the PropertyUnion class.
- The type-checking could get pretty intricate with a more complex inheritance hierarchy, creating a potential source of bugs.
- It's kind of irksome that you have to write the PropertyUnion class in the first place. I mean, it's a very simple implementation and everything that it needs—an inheritance hierarchy to wrap up, and some meta-data—is available to the compiler as type information at build-time.
So now I'll show you how to implement a PropertyUnionBuider using .NET's CodeDom namespace, the library designed for writing language-neutral code-writing code.
But first a word of caution. CodeDom lets you write code that writes code. Even better, your code-writing code can write code in any .NET language. So if you're like me, your first reaction is to say, "Cool!" Unfortunately, it's not always all that cool. In fact, the namespace can be a real hassle to work with for a number of reasons.
- CodeDOM doesn't support any language-specific idioms. The namespace represents the least common denominator of all .NET languages, so you can't directly use handy C# keywords such as as and is. Instead, you'll have to use framework methods, which can add verbosity and reduce readability.
- CodeDOM is verbose. Vertically, it takes many more lines of code to write an algorithm with CodeDom than to write it directly in the .NET language of your choice. For me, the ratio is at least 3:1. But horizontally too, CodeDOM takes up a lot of room because many of the class names are really, really long— CodePropertySetValueReferenceExpression, for example. So even after you get comfortable working with the classes, it's still hard to figure out what's going on inside a block of code because the length of a single line can easily top 150 characters.
- CodeDOM is hard to write. This is the kicker, and it will remain true even if Microsoft were to put a ton of work into making CodeDOM more usable— writing code-generating code is still a very different experience. You're programmatically generating a parse-tree, and that requires a different mindset from standard development.
The point here is that CodeDOM is best used for generating simple code in multiple languages. Building a typed DataSet generator is a great application for CodeDOM, as is the PropertyUnion generator example in this article. But, if you need to generate fancy algorithms, or if you don't need to generate code in multiple languages, then you're probably better off using either XSLT, or a commercial code generator.
My approach here won't be to hold your hand and take you line by line. I tried that, and the result was a very boring and tedious article. It was boring, I think, because CodeDOM code is a fairly tedious way to express concepts that are second nature to most programmers: types, variables, methods, algorithms. And for details, you'll have to look at the sample application
anyway. So instead, I'll take you on a high-level tour of a working CodeDOM application.
Step 1: Some Preliminary Tools
You'll find that you need to do a lot of type-manipulation to work with CodeDOM, so I've included a useful class called InheritanceTree (see Listing 1
) that takes care of all the type-operations. Essentially, it's a typesafe collection of Type objects, with some checks to ensure that users don't try to place types that aren't in the same hierarchy—for example, System.String and SqlDataReader—in the same PropertyUnion class.
You'll also find a utility class, called Build, in the sample code
. (I didn't create the Build class right away, though I wish that I had.) The idea is to wrap up common CodeDom operations—even single line operations—to improve readability.
Take the CodePropertySetValueReferenceExpression class as an example. The CodeDOM approach represents language features as classes. This particular class represents a CodeDOM equivalent of the C# keyword value
used in property setters. But, of course, you can't use the value
keyword because that's C#-specific, so CodeDOM substitutes this class instead.
Using this kind of syntactic sugar is mostly personal preference. But, to me, this:
new CodeAssignStatement(propRef, Build.Value);
Is easier to read than this:
new CodeAssignStatement(propRef, new
Just so you can see what all that boils down to, here's the C# equivalent of the preceding CodeDOM statements:
someExpression = value;