5

How can I instantiate a List<Foo> or List<Bar> at runtime by providing the System.Type value to a constructor? This question has to be answered many times but I can't find it.

Ultimately, I want to make an extension method like so:

public static IEnumerable<T> CreateEnumerable<T>(this System.Collections.IEnumerable list, Type type){
    var stuff = something;

    // magic happens to stuff where stuff becomes an IEnumerable<T> and T is of type 'type'

    return stuff as IEnumerable<T>;
}
Jeremy Holovacs
  • 22,480
  • 33
  • 117
  • 254
  • 2
    possible duplicate of [Create generic List with reflection](http://stackoverflow.com/questions/14888075/create-generic-listt-with-reflection) – Michael Edenfield Mar 02 '13 at 15:58
  • That answer is close, but I don't want it linked to a property. It may be that my understanding of the problem is what is actually the problem. – Jeremy Holovacs Mar 02 '13 at 16:10
  • The actual instantiation of the generic list happens at `Activator.CreateInstance`; what you do with it from there is up to you, including `return newObject as IEnumerable`; the trick is setting up the generic parameters bit correctly with your type. – Michael Edenfield Mar 02 '13 at 18:52

4 Answers4

11

You can specify the parameter of List<> at runtime using reflection and the MakeGenericType method.

var typeParam = typeof(Foo);
var listType = typeof(List<>).MakeGenericType(typeParam);

And then instantiate it using the Activator class

var list = Activator.CreateInstance(listType);

However, if all you're trying to do is turn an IEnumerable into an IEnumerable<T>, Linq already has methods (Cast and OfType) to do this:

IEnumerable untypedList = ...

var foos = untypedList.Cast<Foo>(); // May throw InvalidCastException
var bars = untypedList.OfType<Bar>();
p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
0

Your extension method is already generic. Just call the constructor.

var list = new List<T>();

If you want to convert a nongeneric IEnumerable to a generic one, there are already methods for that in System.Linq.

return list.Cast<T>();
Tar
  • 429
  • 2
  • 5
0

You can't logically make the Type type parameter match the < T > generic argument.

The extension method must return the non-generic IEnumerable.

It is possible to make it so as that this syntactical IEnumerable will in fact (at runtime) hold a generic IEnumerable < That particular type > but the user programmer which executes your extension method must make an assumption, a check and a forced cast.

Beware that you might end up with InvalidCastException if you go down this route and the user programmer isn't aware of things :).

Here it goes:

public static class SomeExtensions {

    private static readonly MethodInfo methodDefOf_PrivateHelper = typeof(SomeExtensions)
        .GetMethod("PrivateHelper", 
                   BindingFlags.NonPublic | BindingFlags.Static, 
                   Type.DefaultBinder, 
                   new [] { typeof(System.Collections.IEnumerable) }, 
                   null);

    private static IEnumerable<T> PrivateHelper<T>(System.Collections.IEnumerable @this){
        foreach (var @object in @this)
            yield return (T)@object; // right here is were you can get the cast exception
    }

    public static System.Collections.IEnumerable DynamicCast(
        this System.Collections.IEnumerable @this,
        Type elementType
    ) {
        MethodInfo particularizedMethod = SomeExtensions.methodDefOf_PrivateHelper
            .MakeGenericMethod(elementType);

        object result = particularizedMethod.Invoke(null, new object[] { @this });
        return result as System.Collections.IEnumerable;
    }


}

And here's how you could use that:

object[] someObjects = new object[] { "one", "two", "three" };
IEnumerable implicitlyCastedToEnumerable = someObjects;

Type unknownType = (DateTime.Now.Hour > 14) ? typeof(string) : typeof(int);

IEnumerable apparentlyNothingHappenedHere 
    = implicitlyCastedToEnumerable.DynamicCast(unknownType);

// if it's not after 14:00, then an exception would've jumped over this line and
// straight out the exit bracket or into some catch clause

// it it's after 14:00, then the apparentlyNothingHappenedHere enumerable can do this:
IEnumerable<string> asStrings = (IEnumerable<string>)apparentlyNothingHappenedHere;

// whereas the earlier would've cause a cast exception at runtime
IEnumerable<string> notGoingToHappen = (IEnumerable<string>)implicitlyCastedToEnumerable;
Eduard Dumitru
  • 3,242
  • 17
  • 31
0

This might do the trick

public static IEnumerable<T> CreateEnumerable<T>(this IEnumerable list, Type type) {
    var stuff=something;

    var temp=stuff;
    stuff=Array.CreateInstance(type, count) as T[]; 
    // copy elements to stuff
    return stuff as IEnumerable<T>;
}

However, nothing is guaranteed that T is of type type. Look at the signature of CreateEnumerable<T>:

public static IEnumerable<T> CreateEnumerable<T>(this IEnumerable list, Type type) 

There is no T from the argument to infer which generic method to invoke, that is, it's required to specify a type parameter when you wish to call it. I would think that T is redundant, and rather

public static IEnumerable CreateEnumerable(this IEnumerable list, Type type) {
    var stuff=something;

    var temp=stuff;
    stuff=Array.CreateInstance(type, count);
    // copy elements to stuff
    return stuff as IEnumerable;
}

Note that I have no idea of the count you would like to have.

Ken Kin
  • 4,503
  • 3
  • 38
  • 76