May 17, 2013

Generating Exception Classes in C# with T4

Being inspired by Marten Range's talk about T4 (Text Template Transformation Toolkit) on dotnetrocks, I decided to try it out for a problem that Marten mentioned in the beginning of the talk and that I have recently encountered at work - generating exception classes in C#. According to MSDN documentation, "derived exception classes should define four constructors". If you have more than one custom exception in your class, coding them manually is a boring task. So, I tried to figure out how much time will it take me to learn how to solve this small problem with T4.

Let's assume we have a program that requires to input string between 1 and 3 characters long, and throws two different exceptions if input is shorter or longer:

static void Main(string[] args)
{
    Console.Write("Input a string longer than 1 chars but shorter than 3 chars: ");
    string input = Console.ReadLine();
    if (input.Length < 2) throw new InputStringTooShortException();
    else if (input.Length > 2) throw new InputStringTooLongException();
    else Console.WriteLine("Thank you!");
}

The first solution that came to my mind was to have a master template file which would use a token instead of exception type name and then specify value for this token by consuming template files.

Let's create the template class that will use provided name of the exception type and generate all the constructors.
ExceptionTemplate.tt:

<#@ output extension=".cs" #>
using System;

[Serializable()]
public class <#= ExceptionTypeName #>Exception : ApplicationException
{
    public <#= ExceptionTypeName #>Exception() : base() { }
    public <#= ExceptionTypeName #>Exception(string message) : base(message) { }
    public <#= ExceptionTypeName #>Exception(string message, Exception inner) : base(message, inner) { }
    protected <#= ExceptionTypeName #>Exception(
        System.Runtime.Serialization.SerializationInfo info,
        System.Runtime.Serialization.StreamingContext context) { }
}

<#+ string ExceptionTypeName = string.Empty; #>

Within this template we used expression block within markers <#= and #> to denote exception type name that is evaluated, converted to a string, and written as an output. We also used class feature syntax within markers <#+ and #> to provide class member ExceptionTypeName that consuming templates will provide as follows:

InputStringTooShortException.tt:

<#@ template language="C#" #>
<# this.ExceptionTypeName = "InputStringTooShort"; #>
<#@include File="ExceptionTemplate.tt" #>

InputStringTooLongException.tt:

<#@ template language="C#" #>
<# this.ExceptionTypeName = "InputStringTooLong"; #>
<#@include File="ExceptionTemplate.tt" #>

Above solution looks straightforward, but the problem with it is that we need one additional template per exception. We can avoid this by making modification to our ExceptionTemplate.tt to have a method that will accept exception type name as a parameter: 

<#@ output extension=".cs" #>
using System;

<#+ void GenerateException(string exceptionTypeName) { #>

    [Serializable()]
    public class <#= exceptionTypeName #>Exception : ApplicationException
    {
        public <#= exceptionTypeName #>Exception() : base() { }
        public <#= exceptionTypeName #>Exception(string message) : base(message) { }
        public <#= exceptionTypeName #>Exception(string message, Exception inner) : base(message, inner) { }
        protected <#= exceptionTypeName #>Exception(
            System.Runtime.Serialization.SerializationInfo info,
            System.Runtime.Serialization.StreamingContext context) { }
    }

<#+ } #>
Now, we need only one template for both exceptions that we want to generate:
GeneratedExceptions.tt:

<#@ template language="C#" #>
<#@include File="ExceptionTemplate.tt" #>

<# GenerateException("InputStringTooLong"); #>
<# GenerateException("InputStringTooShort"); #>

With this solution, we store all exception classes within one file, but if you need to store them in separate files, you can use a trick described in Oleg Sych's blog. By the way, his blog contains lots of information about using T4 ;)


http://www.linkedin.com/in/oldbam

1 comment:

lingmaaki said...

Custom exception sample...

http://net-informations.com/q/faq/custom.html

Ling