简体   繁体   中英

Instantiate a generic collection using a Type argument

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>;
}

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>();

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>();

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;

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM