Find method through Reflection by creating a List<T> of the right type at runtime for CreateInstance argument

I have a base class (MyBase) with lots of possible derived classes (in this example, MyDerived and MyDerived2). Along the way a List<MyBase> is handed off to another class, and then later on that class needs to call a constructor of yet another class... which constructor is called is based entirely on what's in the List.

I can use Reflection to find the constructor, but only if somewhere along the way I do something like:

var r = baseList.ConvertAll(x => (MyDerived1)x);  

So that r contains a List<MyDerived1> and I can find the constructor through Reflection. This is silly, of course, because I could also just bake-in the constructor since I'm bothering to bake in MyDerived1 in that ConvertAll statement. But this is one derived class, and I have dozens.

What I want to do is have the method RunTest() call the right constructor based entirely on what it finds in the List<MyBase> at runtime. Looking at the first object in the list is fine, as a practical matter since they'll always be all of the same type.

Is this possible? How?

namespace Classtest
{
    class Program
    {
        static void Main(string[] args)
        {
            Test t = new Test( new List<MyBase>() {
                            new MyDerived1(),
                            new MyDerived1() } );
            t.RunTest();
        }
    }

    public class Test
    {
        List<MyBase> baseList;
        public Test(List<MyBase> bl)
        {
            baseList = bl;
        }
        public void RunTest()
        {
            // I would like the CreateInstance to figure out which constructor
            //    to call based on what it finds in baseList,
            //   without having to indicate "MyDerived1" here.
            // This works, but I'd rather have it figure this out at
            //   runtime for every single possible derived class...
            var r = baseList.ConvertAll(x => (MyDerived1)x);  
            Object o = Activator.CreateInstance(typeof(CallMe), 
                        new object[] { r });
        }
    }

    public class CallMe
    {
        public CallMe(List<MyDerived1> myDerived)
        {
            Console.WriteLine("Found it.");
        }
        public CallMe(List<MyDerived2> myDerived)
        {
            Console.WriteLine("Wrong one!");
        }
    }

    public class MyBase
    { }
    public class MyDerived1 : MyBase
    { }
    public class MyDerived2 : MyBase
    { }
}

Answers


You can use MakeGenericType() to create a List<T> with the correct generic type:

public void RunTest()
{
    // This method is creating a new list by doing the following:
    // var r = new List<baseList[0].GetType>(
    //             baseList.Cast<baseList[0].GetType()>);

    var elementType = baseList[0].GetType();

    // Get the System.Linq.Enumerable Cast<elementType> method.
    var castMethod = typeof(Enumerable)
                   .GetMethod("Cast", BindingFlags.Public | BindingFlags.Static)
                   .MakeGenericMethod(elementType);

    // Create a List<elementType>, using the Cast method to populate it.
    var listType = typeof(List<>).MakeGenericType(new [] { elementType });
    var r = Activator.CreateInstance(listType, 
            new [] {castMethod.Invoke(null, new [] {baseList})});

    Object o = Activator.CreateInstance(typeof(CallMe), 
               new [] { r });
}

If your CallMe constructors could be changed to take an IEnumerable<> parameter instead of List<>, then you could simplify RunTest() by removing the List<> creation:

public void RunTest()
{
    var elementType = baseList[0].GetType();

    // Get the System.Linq.Enumerable Cast<elementType> method.
    var castMethod = typeof(Enumerable)
                   .GetMethod("Cast", BindingFlags.Public | BindingFlags.Static)
                   .MakeGenericMethod(elementType);

    Object o = Activator.CreateInstance(typeof(CallMe), 
               new [] { castMethod.Invoke(null, new[] {baseList}) });
}

public void RunTest()
{ 
    // it seems like you would want to run the following using reflection...
    // var myBaseList = this.baseList.OfType<this.baseList[0].GetType()>().ToList();

    Type[] genericTypeArray = new Type[] { this.baseList[0].GetType() };

    // call OfType to get IEnumerable<this.baseList[0].GetType()>
    MethodInfo ofTypeMethodInfo = typeof(Enumerable).GetMethods().Where(d => d.Name == "OfType").First();
    ofTypeMethodInfo = ofTypeMethodInfo.MakeGenericMethod(genericTypeArray);
    object myBaseEnumerable = ofTypeMethodInfo.Invoke(null, new object[] { this.baseList });

    // call ToList to get List<this.baseList[0].GetType()>
    MethodInfo toListMethodInfo = typeof(Enumerable).GetMethods().Where(d => d.Name == "ToList").First();
    toListMethodInfo = toListMethodInfo.MakeGenericMethod(genericTypeArray);
    object myBaseList = toListMethodInfo.Invoke(null, new object[] { myBaseEnumerable });

    Object o = Activator.CreateInstance(typeof(CallMe), new object[] { myBaseList }); 
}

If CreateInstance can't find the right constructor, try typeof(...).GetConstructor() and pass in the right types. This will give you the right ctor, which you can invoke. I don't think there's another, non-reflection way, LINQ Cast<> and similar methods require the type to be statically known or given as a type parameter, which is not the case when you have a list which could contain anything.

UPDATE: Oh, it won't work, you can't pass a List<derived> to a List<base> constructor. Here's what you need: you need to call Cast<> with reflection. typeof(Enumerable).GetMethod("Cast").MakeGenericMethod(list[0].GetType()). Then, you have a list with the correct type, so no need for constructor reflection, Activator will work with this.


public void RunTest()         
{            
 if (baseList.Count > 0)
 {
   Type ctorParam = typeof(List<>).MakeGenericType(baseList[0].GetType());
   object instance = typeof(CallMe).GetConstructor(new Type[] { ctorParam })
                            .Invoke(new object[] { baseList });   
 }
} 

Need Your Help

Permission denied using ant and scp

java ant java-7 scp

In my ant file, I scp a java war file to a test server. As soon as I switched over to java 1.7, due to project requirements, the ant file is throwing a permission denied error:

Google Push Notification Does Not Work In some Networks

android networking push-notification

I have an android application (not in the google play) and it is being used by a company (in their building) right now. It uses Google Push Notifications and actually notifications mean everything.

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.