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:
Custom exception sample...
http://net-informations.com/q/faq/custom.html
Ling
Post a Comment