How to use T4 to generate Decorator classes
This article demonstrates how to use Introspection and T4 templates to generate C# Decorator classes in Visual Studio.
Overview
Decorator is a design pattern that allows adding or modifying behavior of an object dynamically (at run-time). Also known as Wrapper, this design pattern offers an alternative to subclassing (or modifying behavior of an object statically, at compile-time). It is best described in the Design Patterns: Elements of Reusable Object-Oriented Software by Eric Gamma, Richard Helm, Ralph Johnson and John Vlissides. Current description of this pattern on Wikipedia is incorrect; a better description can be found here.
While in most cases Decorators contain unique code, they sometimes need to perform mundane and repetitive tasks such as logging, security, exception handling, etc. If you have a large number of different components (such as WCF service contracts) that need decorators performing the same task (such as security/authorization), generating decorator code may be beneficial. This can be accomplished using .NET metadata describing component types.
An obvious first choice for accessing .NET metadata is Reflection. Built into .NET base class library, this API provides access to metadata of .NET types. Unfortunately, Reflection is optimized for code execution. One particular limitation makes it ill-suited for code generation - an assembly loaded using Reflection can only be unloaded with its AppDomain. Because T4 templates are compiled into .NET assemblies and cached to improve performance of code generation, using Reflection to access the component assembly causes T4 to lock it. This prevents you from changing/recompiling the component until you close and reopen the solution. While you can certainly live with this annoyance, it would be nice to avoid it.
Faced with similar challenges, FxCop team developed Introspection engine as an alternative to Reflection. This API was optimized for code analysis and doesn’t lock the assemblies. Included with FxCop starting with version 1.30, this API resides in FxCopSdk.dll and Microsoft.Cci.dll assemblies located in FxCop installation directory. Here is an example of using Introspection engine in FxCop 1.35 to access metadata for IMyComponent interface defined in ComponentLibrary.dll.
using System; using Microsoft.Cci; // Microsoft.FxCop.Sdk in FxCop 1.36 // … AssemblyNode assembly = AssemblyNode.GetAssembly("ComponentLibrary.dll"); TypeNode interface = assembly.GetType( Identifier.For("ComponentLibrary"), // namespace Identifier.For("IMyComponent")); // type name
Having access to the interface metadata, writing a T4 template to generate decorator class that implements it is straightforward.
public class Decorator: <#= interface.FullName #> { // … <# foreach(Member member in interfaceType.Members) WriteMember(member); #> }
Main disadvantage of using Introspection is its lack of documentation. I wouldn’t be able to figure this out without Lutz Roeder’s Reflector. Jason Kresowaty’s web site and John Robbins’ article in MSDN Magazine are also helpful, even though they focus on using Introspection to develop FxCop rules.
Implementation
In order to run this code, you will need Visual Studio 2005 Standard (or higher) Edition, FxCop 1.35 and DSL Tools installed on your computer. You may also want to install T4 Editor by Clarius Consulting, which adds IntelliSense and syntax highlighting to Visual Studio text editor for T4 templates.
Decorator.tt is a T4 template that generates a decorator class. It defines the following parameters:
- InterfaceAssemblyName - path and file name of the assembly that contains component interface definition.
- InterfaceTypeName - full name (including namespace) of the component interface.
- ClassName - name of the decorator class to be generated.
- BaseClassName - name of the decorator’s base class.
- NamespaceName - namespace of the decorator class.
Generated decorator has a public constructor that takes a component instance (defined by InterfaceTypeName) as a parameter. The template uses Introspection to generate a decorator class that implements all interface methods by calling the component. Template contains placeholders marked by TODO comments where you can place place actual decorator code to be called before and after the component call in each method.
The template does not allow the decorator class and the interface to be in the same assembly. Interface must be already compiled before decorator class can be generated.
Usage
In order to use the attached template in your project, follow the steps below.
- Add a copy of Decorator.tt to your project and modify it to produce the desired decorator code. In particular, replace TODO comments with actual code, modify constructor if necessary, etc. Verify template output by looking at Decorator.cs, which will be automatically generated each time you save the template. When done, clear Custom Tool property of Decorator.tt to avoid generating Decorator.cs.
- Add a new text file with .tt extension to the project. Name of the file should match the name of the actual decorator class. Modify this file to use Decorator.tt and provide values for template parameters. For example, TestDecorator.tt contains the following code:
<# NamespaceName = "ConsoleApplication"; ClassName = "TestDecorator"; BaseClassName = "Object"; InterfaceAssemblyName = @"..\ComponentLibrary\bin\Debug\ComponentLibrary.dll"; InterfaceTypeName = "ComponentLibrary.IMyComponent"; #> <#@ include file="Decorator.tt" #>
- Note that InterfaceAssemblyName specifies a path relative to the location of the template file. This template generates the following code in TestDecorator.cs:
// <autogenerated> // This file was generated from T4 template TestDecorator.tt. // </autogenerated> using System; namespace ConsoleApplication { public class TestDecorator: Object, ComponentLibrary.IMyComponent { private readonly ComponentLibrary.IMyComponent _component; public TestDecorator(ComponentLibrary.IMyComponent component) { if (component == null) throw new ArgumentNullException("component"); _component = component; } #region IMyComponent methods public bool TestBooleanMethod() { bool result; // TODO: your code before method call result = _component.TestBooleanMethod(); // TODO: your code after method call return result; } public void TestVoidMethod(string s, ref string r, out string o) { // TODO: your code before method call _component.TestVoidMethod(s, ref r, out o); // TODO: your code after method call } #endregion } }
- Repeat previous step for each concrete decorator you need to generate.
- After changing/recompiling the component interface, you can regenerate a particular decorator by right-clicking it in Solution Explorer and selecting Run Custom Tool from the context menu. You can regenerate all decorators in a project by clicking Transform All Templates button in Solution Explorer.
Known issues
Current version of the attached template contains the following limitations.
- Decoration of properties and events is not supported.
- Decorator boilerplate is embedded Decorator.tt file with the code. To reduce code duplication, consider extracting code into a separate .tt file if you need to have multiple decorator templates. This is not necessary if you simply need to generate multiple decorators from the same template.
About T4
T4 (Text Template Transformation Toolkit) is a template-based code generation engine. It is available in Visual Studio 2008 and as a download in DSL and GAT toolkits for Visual Studio 2005. T4 engine allows you to use ASP.NET-like template syntax to generate C#, T-SQL, XML or any other text files.
For more information about T4, check out my previous article.
Download
T4 templates and source code used in this article are available for download here.


