Code Generation – a Tabu?

I spend most of my days developing object oriented .NET solutions, doing my best to adhere to best practices like the SOLID and DRY principles. Every once in a while, though, I find myself writing repetitive code. Not the kind of code you write in a hurry because of a tight schedule, but repetitive code enforced by the framework or other external conditions.

Enforced Redundancy

One example is custom Exception classes. The interesting bits of a custom Exception class are really only the class name, the base class and any additional data associated with it. Nevertheless, I must always remember to define a handful of constructors and make sure the class is serializable. The result is a collection of classes that follow a redundant pattern of boilerplate code, just because my programming language does not support generalization of this kind of redundancy.

To avoid having to write this code by hand each and every time, it is tempting to define a code snippet in Visual Studio that generates the skeleton for an Exception class. Then, I would only have to fill in the custom bits like the class name and base class. Problem solved! Or?

What if I make a change to my code snippet? Maybe I want a different formatting of the code, or I want to override a method. These changes would naturally not propagate to the code generated with my old snippet. To avoid inconsistency, I now face a tedious task of updating all the existing code, crossing my fingers that further changes will not be required.

What if changes to the snippet template could automatically update all previously generated code…

Code Generation

This is where code generation enters the picture. Since the DRY principle is about maintainability, it only applies to code that has to be maintained. If only the template adheres to the DRY principle, it does not really matter if the generated code is repetitive.

For .NET developers, T4 is the most accessible tool for code generation. T4 is short for Text Template Transformation Toolkit and is built into Visual Studio. It allows my to define some source data and a template which together produce a text file, typically a source code file. The resulting file is added to the project as a sub-item of the template. Any changes to the T4 template will regenerate the entire output file.

Image

Let us consider the issue with custom Exception classes with our new point of view. With T4, I can simply create a template which defines which classes I want and how I want them generated. Such a T4 template can look like this (the portion of the file you typically maintain is highlighted):

<#@ template language="C#" #>
<#@ output extension=".cs" #>
<#
var exceptions = new []
{
	DefineException("Message"),
	DefineException("BadResponse").DerivedFrom("Message"),
	DefineException("InvalidState")
};
//----------------------------------------------------------------------------------
#>
using System;
using System.Runtime.Serialization;

namespace MyNamespace
{
<# foreach(var exception in exceptions) { #>
	[Serializable]
	public partial class <#= exception.ClassName #> : <#= exception.BaseClassName #>
	{
		public <#= exception.ClassName #> () : base () {}
		public <#= exception.ClassName #> (string message) : base (message) {}
		public <#= exception.ClassName #> (string message, Exception inner) : base (message, inner) {}
		protected <#= exception.ClassName #> (SerializationInfo info, StreamingContext context) : base (info, context) {}
	}

<# } #>
}
<#+ 
//----------------------------------------------------------------------------------
ExceptionDefinition DefineException(string name)
{
	return new ExceptionDefinition { Name = name, BaseName = "" };
}

class ExceptionDefinition
{
	public string Name;
	public string BaseName;

	public string ClassName { get { return Name + "Exception"; } }
	public string BaseClassName { get { return BaseName + "Exception"; } }

	public ExceptionDefinition DerivedFrom(string baseName) { BaseName = baseName; return this; }
}
#>

The code generated from this template looks like this:

using System;
using System.Runtime.Serialization;

namespace MyNamespace
{
	[Serializable]
	public partial class MessageException : Exception
	{
		public MessageException () : base () {}
		public MessageException (string message) : base (message) {}
		public MessageException (string message, Exception inner) : base (message, inner) {}
		protected MessageException (SerializationInfo info, StreamingContext context) : base (info, context) {}
	}

	[Serializable]
	public partial class BadResponseException : MessageException
	{
		public BadResponseException () : base () {}
		public BadResponseException (string message) : base (message) {}
		public BadResponseException (string message, Exception inner) : base (message, inner) {}
		protected BadResponseException (SerializationInfo info, StreamingContext context) : base (info, context) {}
	}

	[Serializable]
	public partial class InvalidStateException : Exception
	{
		public InvalidStateException () : base () {}
		public InvalidStateException (string message) : base (message) {}
		public InvalidStateException (string message, Exception inner) : base (message, inner) {}
		protected InvalidStateException (SerializationInfo info, StreamingContext context) : base (info, context) {}
	}

}

Notice that I make use of partial classes from C#. Remember that Visual Studio regenerates the code whenever the template is touched. Hence, we need a way of augmenting the generated types without modifying the generated file:

namespace MyNamespace
{
	public partial class InvalidStateException
	{
		public int StatusCode { get; set; }
	}
}

Code generation has an undeservedly bad reputation, mainly due to many examples of abuse. And understand me right, code generation must not become your golden hammer. If you use it right, however, code generation can drastically improve the maintainability of a code base. It can also make debugging and troubleshooting easier, as generated code typically has fewer abstractions.

The best developers are those who manage to approach problems from multiple angles, looking for the best solution. Next time you want to create a code snippet, consider if code generation might be a suitable solution.

Links

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s