Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cast using Reflection in C#

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

like image 493
mnield Avatar asked Jan 21 '23 09:01

mnield


2 Answers

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;
}
like image 96
Ben Voigt Avatar answered Jan 22 '23 22:01

Ben Voigt


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.

like image 39
Marc Gravell Avatar answered Jan 22 '23 21:01

Marc Gravell