I was recently working on a code generator and I made use of some simple delegates and I thought that I would share a few ideas.
Some folks may be wondering what is a delegate. You can think of a delegate as a type safe function pointer. It's a type which can be passed around like any other type and it can be invoked which calls the original method that it's pointing to. Lets look at a delegate type declaration:
public delegate void DoSomething();
This basically creates a delegate type called DoSomething that can point to a method that takes no arguments and returns void.
Say you have a method:
public void HeresSomething()
{
//put something here
}
then you can create a new instance of our delegate and have it point to that method like:
DoSomething myDelegate = new DoSomething(HeresSomething);
ReSharper tells me that you can abbreviate this syntax like so:
DoSomething myDelegate = HeresSomething;
Now myDelegate can be passed around and then executed just like a method which will call the real method HeresSomething():
This becomes really valuable is when you have delegates that take arguments and return values as you will see in a moment.
In my particular real life case, I have some .NET data transfer objects (DTOs) which I want to code generate equivalent Flex ActionScript objects so I can move data back and forth via serialization. These DTOs can be complex types (objects within objects) so I will need to unwind the object hierarchy to determine what to generate.
Let's look at an example without using delegates:
class TypeUnwinder
{
public void Unwind(Type type, List<Type> allTypes)
{
if (allTypes.Contains(type))
return;
allTypes.Add(type);
UnwindProperties(type, allTypes);
UnwindMethods(type, allTypes);
}
private void UnwindProperties(Type type, List<Type> allTypes)
{
foreach (PropertyInfo propertyInfo in type.GetProperties())
{
Unwind(propertyInfo.PropertyType, allTypes);
}
}
private void UnwindMethods(Type type, List<Type> allTypes)
{
foreach (MethodInfo methodInfo in type.GetMethods())
{
Unwind(methodInfo.ReturnType, allTypes);
UnwindParameters(methodInfo, allTypes);
}
}
private void UnwindParameters(MethodInfo methodInfo, List<Type> allTypes)
{
foreach (ParameterInfo parameterInfo in methodInfo.GetParameters())
{
Unwind(parameterInfo.ParameterType, allTypes);
}
}
}
This code works ok but my real case is much more complex. I actually have to unwind a couple of different times for different reasons and I have multiple checks for attributes and I ignore certain types. I want to reuse my pattern of unwinding with different logic. I can do this using a delegate. Instead of passing a List around, I can have the UnwindType method simply call a delegate that takes a Type. The calling class supplies the delegate and figures out what to do with the Type when it's called.
First I create a delegate that returns bool that takes one argument of type Type:
public delegate bool DoSomethingWithTypeDelegate(Type type);
Then we modify our class by removing the references to the List and have it take in an instance of our delegate in the constructor:
class TypeUnwinder2
{
private readonly DoSomethingWithTypeDelegate doSomethingWithType;
public TypeUnwinder2(DoSomethingWithTypeDelegate doSomethingWithType)
{
this.doSomethingWithType = doSomethingWithType;
}
public void UnwindType(Type type)
{
if(!doSomethingWithType(type))
return;
UnwindProperties(type);
UnwindMethods(type);
}
private void UnwindProperties(Type type)
{
foreach (PropertyInfo propertyInfo in type.GetProperties())
{
UnwindType(propertyInfo.PropertyType);
}
}
private void UnwindMethods(Type type)
{
foreach (MethodInfo methodInfo in type.GetMethods())
{
UnwindType(methodInfo.ReturnType);
UnwindParameters(methodInfo);
}
}
private void UnwindParameters(MethodInfo methodInfo)
{
foreach (ParameterInfo parameterInfo in methodInfo.GetParameters())
{
UnwindType(parameterInfo.ParameterType);
}
}
}
Now a calling class can control what actually happens when a Type is hit (remember, this is just example code):
class Program
{
static readonly List<Type> types = new List<Type>();
static void Main(string[] args)
{
TypeUnwinder2 unwinder = new TypeUnwinder2(IndexType);
unwinder.UnwindType(typeof(SomeCustomTypeToUnwind));
foreach (Type type in types)
{
Console.WriteLine(type.FullName);
}
}
static bool IndexType(Type type)
{
if (!types.Contains(type))
{
types.Add(type);
return true;
}
return false;
}
}
Notice how we pass in IndexType to the TypeUnwinder2 constructor (the delegate is created for you). If we wanted to see the delegate, we could have done:
TypeUnwinder2 unwinder = new TypeUnwinder2(new DoSomethingWithTypeDelegate(IndexType));
So adding a delegate allowed me to decouple my unwinding pattern from the work that happens as a result of the unwinding. Yeah!
Here's a version that uses another delegate to reuse a single foreach loop. It's a bit ridiculous , but it might be fun to decipher.
class TypeUnwinder3
{
private DoSomethingWithTypeDelegate doSomethingWithType;
public TypeUnwinder3(DoSomethingWithTypeDelegate doSomethingWithType)
{
this.doSomethingWithType = doSomethingWithType;
}
public void UnwindType(Type type)
{
if (!doSomethingWithType(type))
return