From time to time you bump into legacy code, where somebody thought that static methods was the solution to all your problems. We all know that an architecture with static methods calling static methods is very tightly coupled, and we would like decouple it.
What I would like to do is make an interface, with all public methods, and make an adapter that implements the interface and uses the static methods of the legacy class.
EDIT:
Of course I should mention that this is a Proof of Concept, hence the code is not complete nor perfect:
- The generated code uses the long form of the built in types instead of the short form
- There are duplicates distributed all around the code
- The code does not work with out/ref parameters (there were no usage of the two in the code I needed code generation for)
- The classname, namespace and assemblylocation should have been T4 properties
This was just to put an example of T4 usage out.
So here is an example of a legacy class:
namespace t4ConsoleApp
{
class StaticStuff
{
public static string Test()
{
return "test";
}
public static int GetNumber()
{
return 42;
}
public static bool IsAbove42(int x)
{
if(x > 42)
return true;
return false;
}
}
}
So what i would like is something like this:
namespace t4ConsoleApp
{
public interface IStaticStuff
{
System.String Test();
System.Int32 GetNumber();
System.Boolean IsAbove42(System.Int32 x);
}
}
namespace t4ConsoleApp
{
public class StaticStuffAdapter : IStaticStuff
{
public System.String Test()
{
return StaticStuff.Test();
}
public System.Int32 GetNumber()
{
return StaticStuff.GetNumber();
}
public System.Boolean IsAbove42(System.Int32 x)
{
return StaticStuff.IsAbove42(x);
}
}
}
Creating this in a manual way could be big work, so I decided to do it using T4.
First I make a class that can do reflection over the legacy class:
using System;
using System.Text;
using System.Reflection;
namespace t4ConsoleApp
{
public class StaticReflection
{
public Type GetType(string assemblyLocation, string typeName)
{
Assembly assembly = Assembly.LoadFrom(assemblyLocation);
if (assembly == null)
{
throw new Exception("assembly not found");
}
Type tp = assembly.GetType(typeName);
if (tp == null)
throw new Exception("type not found");
return tp;
}
public ClassInfoValue GetAllPublicStaticMembers(Type mytype)
{
ClassInfoValue classInfoValue = new ClassInfoValue(7);
MethodInfo[] miArray = mytype.GetMethods(BindingFlags.Public
| BindingFlags.Static);
foreach (MethodInfo mi in miArray)
{
string parameterNames = GetParameterNames(mi);
string parametersWithNames = GetParametersWithNames(mi);
classInfoValue.ParameterNamesList.Add(parameterNames);
classInfoValue.Returntypes.Add(mi.ReturnType.ToString());
classInfoValue.MethodNames.Add(mi.Name);
classInfoValue.ParameterswithNamesList.Add(parametersWithNames);
}
return classInfoValue;
}
private static string GetParameterNames(MethodInfo mi)
{
ParameterInfo[] piArray = mi.GetParameters();
StringBuilder sb = new StringBuilder();
int i = 0;
foreach (ParameterInfo info in piArray)
{
if (i == 0)
{
sb.Append(info.Name);
}
else
{
sb.Append(", " + info.Name);
}
i++;
}
return sb.ToString();
}
private static string GetParametersWithNames(MethodInfo mi)
{
ParameterInfo[] piArray = mi.GetParameters();
StringBuilder sb = new StringBuilder();
int i = 0;
foreach (ParameterInfo info in piArray)
{
if (i == 0)
{
sb.Append(info.ParameterType + " " + info.Name);
}
else
{
sb.Append(", " + info.ParameterType + " " + info.Name);
}
i++;
}
return sb.ToString();
}
}
}
And lastly the T4 template to do this, I made a console app to contain the T4 stuff
<#@ template language="C#v2.0" hostspecific="True" #>
<#@ output extension=".generated.cs" #>
<#@ assembly name="system.dll" #>
<#@ assembly name="D:\Src\t4consoleapp\t4consoleapp\bin\debug\t4ConsoleApp.exe" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="t4ConsoleApp" #>
<#
string classname = "StaticStuff";
string nameSpace= "t4ConsoleApp";
string classNameWithNamespace = nameSpace + "." + classname;
string assemblylocation = @"D:\Src\t4consoleapp\t4consoleapp\bin\debug\t4ConsoleApp.exe";
StaticReflection staticReflection = new StaticReflection();
Type myType = staticReflection.GetType(assemblylocation, classNameWithNamespace);
ClassInfoValue classInfo = staticReflection.GetAllPublicStaticMembers(myType);
#>
<#= "//Generated at: " + DateTime.Now #>
using System;
namespace <#= nameSpace #>
{
public interface I<#= classname #>
{
<# for(int i = 0; i< classInfo.MethodNames.Count; i++){ #>
<# if(classInfo.Returntypes[i] == typeof(void).ToString()) {#>
void <#= classInfo.MethodNames[i] #>(<#= classInfo.ParameterswithNamesList[i] #>);
<#} else {#>
<#= classInfo.Returntypes[i] #> <#= classInfo.MethodNames[i] #>(<#= classInfo.ParameterswithNamesList[i] #>);
<# } #>
<# } #>
}
}
namespace <#= nameSpace #>
{
public class <#= classname #>Adapter :I<#= classname #>
{
<# for(int i = 0; i< classInfo.MethodNames.Count; i++){ #>
<# if(classInfo.Returntypes[i] == typeof(void).ToString()) {#>
public void <#= classInfo.MethodNames[i] #>(<#= classInfo.ParameterswithNamesList[i] #>)
{
<#= classname #>.<#= classInfo.MethodNames[i] #>(<#= classInfo.ParameterNamesList[i] #>);
}
<#} else {#>
public <#= classInfo.Returntypes[i] #> <#= classInfo.MethodNames[i] #>(<#= classInfo.ParameterswithNamesList[i] #>)
{
return <#= classname #>.<#= classInfo.MethodNames[i] #>(<#= classInfo.ParameterNamesList[i] #>);
}
<# } #>
<# } #>
}
}
namespace <#= nameSpace #>
{
public class <#= classname #>Factory
{
public I<#= classname #> Get<#= classname #>()
{
return new <#= classname #>Adapter();
}
}
}