views:

547

answers:

2

I have an array of Castle windsor registration components of type IRegistration[]

In this case ComponentRegistration<T> : IRegistration

For each element in my array, if it can be upcast to ComponentRegistration<> I would like to upcast it back to ComponentRegistration<T> and process it. How exactly would I do this?

I got as far as

foreach (var r in registrations) {
  if(typeof(ComponentRegistration<>).IsAssignableFrom(r.GetType())) {
    var cr = CAST r SOMEHOW
    DoStuff(cr);
  }
+1  A: 

Unfortunately you cannot do this without either

  1. Having a non-generic form of ComponentRegistration
  2. Knowing T
  3. Using a reflection hack

EDIT the reflection hack

Essentially you'll need to use reflection to grab the runtime type. Inspect the type to grab the generic T in ComponentRegistration. Then use that T to instantiate an instance of method DoStuff and pass in the object as a parameter.

JaredPar
what kind of reflection hack? This is for a module to be run to assist with debugging so I have no problem with hacking it.
George Mauer
Thanks. I'm surprised that there isn't a set of OS libraries to do this sort of thing already.
George Mauer
+2  A: 

Surely if you've got an IRegistration[] then you're downcasting rather than upcasting.

However, to get to your problem, what does DoStuff() look like? Does it need to know the type argument for ComponentRegistration<T>? If not, you might be best creating a non-generic base class:

public abstract class ComponentRegistration : IRegistration
{
    // Anything in the original API which didn't use T
}

public class ComponentRegistration<T> : ComponentRegistration
{
    // The bits which need T
}

Then you could write:

foreach (var r in registrations)
{
    ComponentRegistration cr = r as ComponentRegistration;
    if (cr != null)
    {
        DoStuff(cr);
    }
}

If you really need DoStuff to use the generic information, you'll have to use reflection to get the appropriate type and invoke it. Avoid if possible :)

EDIT: Okay, here's an example of the reflection nastiness. It doesn't try to account for generic interfaces, as that gets even hairier.

using System;
using System.Reflection;

class Test
{   
    static void Main()
    {
        Delegate[] delegates = new Delegate[]
        {
            (Action<int>) (x => Console.WriteLine("int={0}", x)),
            (Action<string>) (x => Console.WriteLine("string={0}", x)),
            (Func<int, int>) (x => x + 1)
        };

        MethodInfo genericPerformAction = typeof(Test).GetMethod
                                                       ("PerformAction");

        foreach (Delegate del in delegates)
        {
            Type t = DiscoverTypeArgument(del, typeof(Action<>));
            if (t == null)
            {
                // Wrong type (e.g. the Func in the array)
                continue;
            }
            MethodInfo concreteMethod = genericPerformAction.MakeGenericMethod
                (new[] { t } );
            concreteMethod.Invoke(null, new object[] { del });
        }
    }

    public static void PerformAction<T>(Action<T> action)
    {
        Console.WriteLine("Performing action with type {0}", typeof(T).Name);
        action(default(T));
    }

    /// <summary>
    /// Discovers the type argument for an object based on a generic
    /// class which may be somewhere in its class hierarchy. The generic
    /// type must have exactly one type parameter.
    /// </summary>
    /// <returns>
    /// The type argument, or null if the object wasn't in
    /// the right hierarchy.
    /// </returns>
    static Type DiscoverTypeArgument(object o, Type genericType)
    {
        if (o == null || genericType == null)
        {
            throw new ArgumentNullException();
        }
        if (genericType.IsInterface ||
            !genericType.IsGenericTypeDefinition || 
            genericType.GetGenericArguments().Length != 1)
        {
            throw new ArgumentException("Bad type");
        }

        Type objectType = o.GetType();
        while (objectType != null)
        {
            if (objectType.IsGenericType &&
                objectType.GetGenericTypeDefinition() == genericType)
            {
                return objectType.GetGenericArguments()[0];
            }
            objectType = objectType.BaseType;
        }
        return null;
    }
}

EDIT: Note that if you're in a situation where all the members are derived from the relevant class, and if you're using C# 4, you can use dynamic binding:

using System;
using System.Reflection;

class Test
{   
    static void Main()
    {
        Delegate[] delegates = new Delegate[]
        {
            (Action<int>) (x => Console.WriteLine("int={0}", x)),
            (Action<string>) (x => Console.WriteLine("string={0}", x)),
            (Action<long>) (x => Console.WriteLine("long={0}", x)),
        };

        foreach (dynamic del in delegates)
        {
            // Yay for dynamic binding
            PerformAction(del);
        }
    }

    public static void PerformAction<T>(Action<T> action)
    {
        Console.WriteLine("Performing action with type {0}", typeof(T).Name);
        action(default(T));
    }
}

Unfortunately I don't know of any way of testing whether it's going to manage to bind to the method successfully without just trying it and catching the relevant exception (which would be grim). Maybe Eric will be able to tell us :)

Jon Skeet
I really feel like it does not need to know the generic information, DoStuff() would attach an interceptor to be used for logging debug info. Unfortunately the Castle Windsor interface only exposes the appropriate methods to do this on the generic class. Can you point me in the direction of the generic hack? I'm running low on time and don't want to go down the wrong path.
George Mauer
Okay - will come up with equivalent using Delegate and Action<T> as the types involved.
Jon Skeet
Wow, thanks Jon. You're like this site's guardian angel
George Mauer