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);
    }
}
Technorati Tags: ,
Good Code Is Not Just About Writing Lines of Code