tags:

views:

486

answers:

4

I am generating a List<T> with a runtime-determined type parameter. I'd like to invoke the ForEach method to iterate over the items in the list:

//Get the type of the list elements
Type elementType = GetListElementType(finfo);

Type listType = Type.GetType("System.Collections.Generic.List`1[" 
                              + elementType.FullName + "], mscorlib", true);

//Get the list
var list = getList.Invoke(null, new Object[] { finfo.GetValue(myObject) });

MethodInfo listForEach = listType.GetMethod("ForEach");

//How do I do this?  Specifically, what takes the place of 'x'?
listForEach.Invoke(list, new object[] { delegate ( x element )
                            {
                                //operate on x using reflection
                            }
                        });

Given a MethodInfo corresponding to the ForEach method contained in my runtime-generated list type, what's the proper way to invoke it using an anonymous method? The above is my first stab, but don't know how to declare the type of the anonymous method's parameter.

+4  A: 

You can do this:

var someGenericListYouCreated = ...;
var enumerable = someGenericListYouCreated as IEnumerable;

foreach(var foo in enumerable){
    ...
}

However, I'm working on way to do what you ACTUALLY want.

Edit:

Right, I hope this makes sense

private class Adapter<T>
{
    private readonly Action<object> act;

    public Adapter(Action<object> act){
        this.act = act;
    }

    public void Do(T o)
    {
        act(o);
    }
}

public static void Main(string[] args)
{
    Type elementType = typeof(string);

    var genericType = typeof(List<>).MakeGenericType(elementType);
    var list = Activator.CreateInstance(genericType);

    var addMethod = list.GetType().GetMethod("Add");
    addMethod.Invoke(list, new object[] { "foo" });
    addMethod.Invoke(list, new object[] { "bar" });
    addMethod.Invoke(list, new object[] { "what" });

    Action<object> printDelegate = o => Console.WriteLine(o);

    var adapter = Activator.CreateInstance(typeof(Adapter<>).MakeGenericType(elementType), printDelegate);
    var adapterDo = adapter.GetType().GetMethod("Do");

    var adapterDelegate = Delegate.CreateDelegate(typeof(Action<string>), adapter, adapterDo);

    var foreachMethod = list.GetType().GetMethod("ForEach");

    foreachMethod.Invoke(list, new object[] { adapterDelegate });
}

What this does:

  1. Create a generic list (notice that it's using typeof(List<>)
  2. Add some strings to this list
  3. Create a new delegate, in this case an Action
  4. Create an instance of the adapter class
  5. Create a delegate of the type we need (Action) on the adapter class
  6. Call ForEach

Note, you don't necessarily have to use Action if you know the type you're going to be processing. You could very easily use an Action and it'll work...

jonnii
Nice. +1 I like that.
Simon P Stevens
A: 

There are a number of issues here.

First, getting the type object of the type with the generic parameter by constructing the name of the type with string concatenation is a bit dodgy. You should do this instead:

Type genericType = typeof(List<>).MakeGenericType(new Type[] { listType });

This is a lot cleaner. Now, to invoke the ForEach method of an instance of this type, you will need an Action<T> object. It's not entirely obvious for me if you want to use a lambda expression or a function pointer, but the point is that you pass the instance of Action<T> just like a normal parameter:

methodInfo.Invoke(list, new object[] { x });

If you could clarify the origin of x (that is, the Action<T> parameter) maybe we could give more help.

DrJokepu
A: 

I couldn't make that an anonymous method works, but, if you have a defined method to execute when ForEach is called and it's overloaded to the possible data types it would work

class Program
{
    static void Main(string[] args)
    {
        var sampleElement = "Foo";

        Type listType = typeof(List<>).MakeGenericType(sampleElement.GetType());
        Type actionType = typeof(Action<>).MakeGenericType(sampleElement.GetType());

        var list = Activator.CreateInstance(listType);
        var action = Delegate.CreateDelegate(actionType, null, typeof(Program).GetMethod ("ForEach"));

        (list as List<string>).AddRange (new []{ "1", "2" });

        listType.GetMethod ("ForEach").Invoke (list, new object []{ action });
    }

    public static void ForEach(string item)
    {
        Console.WriteLine(item);
    }
}
AlbertEin
A: 

Using generics is cool because it saves you a lot of coding and there's a theoretical gain in performance because it avoids boxing and unboxing, and so on, but I don't see the need to use dynamically created generics, using reflection and very slow methods.

Why don't you use a a simple IList interface and a foreach loop?

jmservera
This is a good point, but there are times when it's useful, for example if you're dealing with unstructured data or runtime created classes.
jonnii