4

I've created a generic function as below (just a s a proof) that will take a List<T> collection and reverse it, returning a new List<T> as its output.

public static List<T> ReverseList<T>(List<T> sourceList)
{
    T[] outputArray = new T[sourceList.Count];
    sourceList.CopyTo(outputArray);
    return outputArray.Reverse().ToList();
}

The purpose of the proof is that I only know what T is at runtime. I am therefore using reflection to call the above method as follows:

List<int> myList = new List<int>() { 1, 2, 3, 4, 5 }; // As an example, but could be any type for T

MethodInfo myMethod = this.GetType().GetMethod("ReverseList");
MethodInfo resultMethod = myMethod.MakeGenericMethod(new Type[] { typeof(int) });
object result = resultMethod.Invoke(null, new object[] { myList });

There are two problems here:

  1. In the second line, rather than supplying typeof(int), I would like suppliy somthign akin to myList.GetType().GetGenericArguments()[0].GetType() in order to make things more flexible because I do not know T until runtime. Doing this results in a runtime error when the Invoke runs as follows: "Object of type 'System.Collections.Generic.List'1[System.Int32]' cannot be converted to type 'System.Collections.Generic.List'1[System.RuntimeType]'."
  2. The result of the Invoke() method returns an object. When debugging, I can see that the object is of type List, but attempting to use it tells me that I have an invalid cast. I assume that I need to use reflection to box the result in to the correct type (i.e. in this example, the equivalent of (result as List<int>).

Does anyone have any pointers that could help me resolve this? Apologies if this is not to clear, I can probably provide more detail if asked.

TIA

10
  • Wait... so why don't you just say myList.GetType().GetGenericArguments()[0].GetType()? Commented Feb 28, 2011 at 15:44
  • Is the code in the second box intended to be inside a generic function, or are you planning to actually pass around an instance of Type? Commented Feb 28, 2011 at 15:46
  • Ah, because that results in the exception as follows: "Object of type 'System.Collections.Generic.List1[System.Int32]' cannot be converted to type 'System.Collections.Generic.List1[System.RuntimeType]'." Commented Feb 28, 2011 at 15:47
  • @siride no The code in the second box is calling the generic method using reflection. Line 3 is about to be corrected as @Ben Voigt has an answer for that part. Commented Feb 28, 2011 at 15:58
  • @mnield well then I fail to see what the problem is. You already know the type of the list, which is int. No need for reflection solutions. Commented Feb 28, 2011 at 16:01

3 Answers 3

5

You've got one GetType() too many. Happens to everyone.

myList.GetType().GetGenericArguments()[0] IS a System.Type -- the one you're looking for.

myList.GetType().GetGenericArguments()[0].GetType() is a System.Type describing System.Type (well, actually the concrete subclass System.RuntimeType).


Also, your ReverseList function is serious overkill. It does an extra copy just to avoid calling List.Reverse. There's a better way to circumvent that:

public static List<T> ReverseList<T>(List<T> sourceList)
{
    return Enumerable.Reverse(sourceList).ToList();
}

or

public static List<T> ReverseList<T>(List<T> sourceList)
{
    var result = new List<T>(sourceList);
    result.Reverse();
    return result;
}

or

public static List<T> ReverseList<T>(List<T> sourceList)
{
    var result = new List<T>();
    result.Capacity = sourceList.Count;
    int i = sourceList.Count;
    while (i > 0)
        result.Add(sourceList[--i]);
    return result;
}
Sign up to request clarification or add additional context in comments.

2 Comments

You sir, are a gentleman and a scholar! That's exactly what I've done there. +1 for fixing the first problem.
As an aside, yes the method was overkill. It's not really the method that I was bothered about, but the way in which it was called and the result dealt with. In reality ReverseList<T> could have just returned the same list as far as this demo is concerned.
3

To access it as a List<T>, yes you'd need to find T using reflection (probably over the interfaces, for example typeof(IList<>), and use more reflection and MakeGenericMethod etc. In all honesty, it isn't worth it: you would do better to check for the non-generic IList:

var list = result as IList;
if (list != null)
{
    // loop over list etc
}

Generics ad reflection are not good friends.

Note in 4.0 there are also some tricks you can do here with dynamic and generics.

4 Comments

OK, maybe I'm being a little dim. I can find the type of T just fine, what I am having trouble doing is casting it so that I can assign it to things. I.e. List<int> jeffTheVariable = result; will not compile as I cannot implicitly cast Object to List<int>. I appreciate Generics and Reflection don't get on too well, but the library in my target application has a large number of classes that I really need to automate.
Generics and reflection get along just fine in my experience, if you have some sample code to look at and serious attention to detail. Of course, you've done some really tricksy stuff with them, so you ought to know better than I do, but I think you may be biased because of the really hard problems you've attacked.
Biased, me... yeah - probably :) The bit that gets me is that it's real easy to get 90% of the way and the last 10% always has me scratching my head/pulling my hair out.
@Ben other way around - having used them much, I think in many cases this approach is overkill; just going non-generic works fine for 95%+ of such cases, and is simpler to get right.
1

The result of the Invoke() method returns an object. When debugging, I can see that the object is of type List, but attempting to use it tells me that I have an invalid cast. I assume that I need to use reflection to box the result in to the correct type (i.e. in this example, the equivalent of (result as List).

The only workaround for this is I can think of is to pass an empty list as the second parameter of the method and to populate that list - the reference returned by Invoke() will always be only of type object, but inside the generic method you do have access to the type itself:

List<int> reverseList = new List<int>();
resultMethod.Invoke(null, new object[] { myList, reverseList });

...

public static void ReverseList<T>(List<T> sourceList, List<T> resultList)
{
    T[] outputArray = new T[sourceList.Count];
    sourceList.CopyTo(outputArray);
    resultList.AddRange(outputArray.Reverse());
}

1 Comment

That's a blindingly simple approach. I think this may be a "Can't see the wood for the trees" moment. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.