A Simple Delegate Example
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():
myDelegate();
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; UnwindItems(type.GetProperties(), UnwindProperty); UnwindItems(type.GetMethods(), UnwindMethod); } private void UnwindItems<T>(IEnumerable<T> items, Action<T> process) { foreach (T item in items) { process(item); } } private void UnwindProperty(PropertyInfo propertyInfo) { UnwindType(propertyInfo.PropertyType); } private void UnwindMethod(MethodInfo methodInfo) { UnwindType(methodInfo.ReturnType); UnwindItems(methodInfo.GetParameters(), UnwindParameter); } private void UnwindParameter(ParameterInfo parameterInfo) { UnwindType(parameterInfo.ParameterType); } }