简体   繁体   中英

Dynamic type with lists in c#

I have a little problem for you guys.

I would like to do this:

Type[] classes = new Type[]{ Class1, Class2 };

foreach(Type t in classes){   
  List<t> list = new List<t>(); 
}

Is there some way to do this?

You cannot cast to a generic type at runtime, because the type in the generic needs to be resolved at compile time.

You can create a generic type in a dynamic manner using reflection, but unless you hard-code the cast, all you get in return is an object.

I cannot really tell what it is you want, I have to agree with a comment this is an XY problem . I would be inclined to make the presumptuous statement that there is a design issue somewhere that this is trying to solve, instead of addressing the design issue directly, or asking the question of what you are trying to achieve directly.


You can use the following code to create the type, then the dynamic type can be used to duck type the various members of List<T> without knowing/caring that it is a list or what T is:

using System;
using System.Collections.Generic;

namespace ConsoleApplication61
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic o = CreateGeneric(typeof(List<>), typeof(int));
            o.Add(1);
            Console.WriteLine(o[0]);
            Console.Read();
        }

        public static object CreateGeneric(Type generic, Type innerType, params object[] args)
        {
            System.Type specificType = generic.MakeGenericType(new System.Type[] { innerType });
            return Activator.CreateInstance(specificType, args);
        }
    }
}

The above sample duck types the Add method and the Indexer. The DLR does the type handling and the duck typing at runtime - knowing that 1 is an int , for example.

Just to clarify, I likely wouldn't use such code in production (unless you requirements are very specific to need this) and any issues with type-mismatching will occur at run time; so you either need to type very accurately (limited IntelliSense) or have good error handling.

Thanks to this blog post for the CreateGeneric method.

This assumes .NET 4 with the new CLR. As @MartinLiversage has also pointed out, this particular sample assumes that you are utilising the list in a sort-of-strongly-typed manner. In my example I am passing an int to a List<int> hidden in a dynamic .


We have been on .NET 4 almost since it was released. We have a large application with an even larger code base. dynamic isn't used once in the application, and only a few times in the test code base. That isn't to say "don't use it", it's to say "most of the time, you don't need it".

You can do it like this:

foreach(Type t in classes)
{
   var listType = typeof(List<>).MakeGenericType(t);
   var instance = Activator.CreateInstance(listType);
}

This is possible with the Type.MakeGenericType Method

Here's a nifty method I found that should work for ya: CodeRef

public static object CreateGeneric(Type generic, Type innerType, params object[] args)
{
    System.Type specificType = generic.MakeGenericType(new System.Type[] { innerType });
    return Activator.CreateInstance(specificType, args);
}

And use it like so:

var o = CreateGeneric(typeof(List<>), t);

Unfortunately, to add items you'll have to do it like so (where item is the item you're adding).

MethodInfo addMethod = o.GetType().GetMethod("Add");
addMethod.Invoke(o, new object[] { item.ToType(t) });

Or use the Generic type as mentioned in another answer.

You can try this:

Type[] classes = new Type[] { typeof(A), typeof(B) };           
foreach (Type t in classes)
{
     Type genericType = typeof(List<>).MakeGenericType(t);
     var list = Activator.CreateInstance(genericType);
}

If you want to keep to objects in one list then probably they have something in common.

In such case, I would argue that, just like LB mentioned in a comment, you are asking a wrong question here.

Probably it's a design issuethat you have. Think about those types, see what they have in common and think about deriving from one base type or make both implement the same interface. In such case you would be able to instantiate a list of the objects of the base type/interface and work with those.

Just to give you a head start:

    abstract class Vehicle {
        public int NumberOfWheels { get; set; }
    }

    class Car : Vehicle
    {
        public Car()
        {
            NumberOfWheels = 4;
        }
    }

    class Bicycle : Vehicle
    {
        public Bicycle()
        {
            NumberOfWheels = 2;
        }
    }

    static void Main(string[] args)
    {

        var v1 = new Car();
        var v2 = new Bicycle();

        var list = new List<Vehicle>();
        list.Add(v1);
        list.Add(v2);

        foreach (var v in list)
        {
            Console.WriteLine(v.NumberOfWheels);
        }

        Console.ReadKey();
    }

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