Udostępnij za pośrednictwem


Is your T4 template a composition?

Maybe every T4 template can’t be code poetry, but pretty much anyone who’s using T4 can get cleaner code and be more productive by using T4’s features around composing chunks of reusable code together.

 

An oft-cited criticism of template-based code generators is that, over time, the templates tend towards spaghetti code; without some effort I think it’s a pretty valid stick to beat T4 and its ilk with.  But here’s the good news:  with not much effort, you can build up a library of chunks that you can compose to get radically more readable and quicker to produce templates.  The common analogy here is Lego (or Legos as my American colleagues endearingly refer to them), but actually, I’m going with bricks and mortar as my analogy.

OK, let’s look at an example here to see what I’m talking about.  Here’s a template I’m using at present to generate all the boilerplate code necessary to create new serializable Exception classes.

 

image

 

This generates a bunch of files with code like the example below (click through for the full code – it’s a bit large to insert)

image

 

This template is following the pattern of setting up a simple data structure to drive the code generator and then walking that structure.  In this case, it’s using the CodeModel from Visual Studio’s DTE automation interface to find exception classes marked with a specific attribute in the current project, and then populating a simple enumerable.   It then walks that enumerable, creating a new file for each exception, and populating it with a nice header, a namespace and the exception partial class.  Normally, all that logic would make for a complex template, where the exception code would be a bit hard to find when you actually wanted to change the generated output.  Here, with the inclusion of some pre-canned functionality and a procedural decomposition style, it’s pretty easy to see where the header, the namespace and its associated using statements and the main exception code all get written.  At this point, OO purists might be wearing a somewhat pained expression, but I’m really applying principals we normally associate with OO, such as single responsibility principle and applying them to my procedural world here (at least as far as the included helper files at the top of our example go.

What we end up with as input to this template is a very trivial handwritten exception declaration, and the code generator takes care of everything else.

image

So what’s the typical structure of such a helper? Let’s take a look at the simplest of the four I use here, the helper that lets me output a standard code gen header, StandardHeader.cs.t4:

image

As you can see – very simple, and very single purpose.  It’s just a definition in a class feature block of a method to write out a header.  A lot of my helpers are just like this – either one method or a small group of related methods to spit out common chunks of boilerplate, perhaps with a few simple parameters.

The key point here is that although the class feature block defines the method signature in C# control block code, it easily switches back to encapsulating boilerplate code without reverting to using clumsy “this.WriteLine()” methods.  This is one of T4’s strongest points, allowing me to very easily modify that header code with a simple cut and paste from a newer example that I like better.

OK, this is getting to be a long post so I’ll take a break.  Next time I’ll look at the Exceptions.cs.t4 helper and talk about keeping helpers decoupled using the approach I described as bricks and mortar.

 

Gareth Jones – Architect – Visual Studio

Technorati Tags: T4,Code Generation,Best Practices