How to generically instantiate a generic class in

2019-04-15 21:05发布

问题:

Maybe it is pretty simple, but I'm not in use with the type Type and it's uses.

Say I want to create a List<T> either with T=Double or T=UInt32, depending on the result of some function, say public static Type CheckType(String input);

In code:

Type t = Program.CheckType(someInput); // it returns typeof(Double) or typeof(UInt32)
if (t == typeof(Double))
    List<Double> l = new List<Double>();
else
    List<UInt32> l = new List<UInt32>();

I know the above code won't compile because I'm letting l to have two different meanings (a list of double and a list of unsigned int)... So it leads to my question:

  • Is there a way to avoid the if in the above code?

Something similar to this:

Type t = Program.CheckType(someInput);
List<t> l = new List<t>(); // I know this won't compile either...

I mean, that would generically instantiate a generic List...

Thank you in advance!

EDIT:

This is NOT a duplicate of the marked question for only one reason: Double and UInt32 are not Anonymous types! The question, here, is how to determine the type of some input data (which will be Type T=typeof(Double) or Type T=typeof(UInt32), for example) and, thus, create a generic SampleClass<T> based on that input data type, T!

In other words: determine some Type T in the runtime and, then, instantiate a generic type with the determined type T. Sorry if I didn't make that clear before...

PS: I used List<T> as an example for SampleClass<T>.

回答1:

You can't type the list as generic, since you don't know the type parameter, but you can create a List instance at runtime.

Type t = Program.CheckType(someInput);
Type listType = typeof(List<>).MakeGenericType(t);
IList list = (IList) Activator.CreateInstance(listType);

If you try to add an object of the incorrect type, you will get an exception, so this approach has an advantage over using a collection like ArrayList, or List<object>



回答2:

There's no real reason to use generics in such instances. Since the generic argument isn't known at compile time the compiler can't verify whether or not the objects you're trying to add or remove are appropriate for that list.

If at all possible, it's best to avoid this situation entirely, possibly through making the method that this code is in generic itself.

If that's not possible, it's probably better to just use the non-generic ArrayList or a List<object>, because using a generic list in this context will add a lot of extra work for no extra help.



回答3:

MakeGenricType might work

Using Reflection to set a Property with a type of List<CustomClass>

    Type type = Program.CheckType(someInput);
    IList list = (IList) Activator.CreateInstance(typeof(List<>).MakeGenericType(type));
    object obj = Activator.CreateInstance(type);
    list.Add(obj);


回答4:

Type t = Program.CheckType(someInput);
var l = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(t)); 


回答5:

I would go for a generic Histogram<T> but not try to hold both types in the same variable, unless you want to just have a IDictionary variable.

Here is an example with using the histogram with double types:

class Program
{

    static Random rnd=new Random();
    static void Main(string[] args)
    {
        Historgram<double> hist=new Historgram<double>();
        for(int i=0; i<1000; i++)
        {
            double x=Math.Round(rnd.NextDouble(), 1);
            hist.Add(x);
        }
        //double[] values=hist.Values;

        Console.WriteLine("Histogram");
        Console.WriteLine("{0,12} {1,12}", "Value", "Quantity");            
        for(int i=0; i<=10; i++)
        {
            double x=(i/10d);
            Console.WriteLine("{0,12} {1,12}", x, hist[x]);
        }
        Console.ReadLine();
    }

with result

   Histogram
   Value     Quantity
       0           52
     0.1           97
     0.2          117
     0.3           98
     0.4           93
     0.5          110
     0.6           97
     0.7           94
     0.8           98
     0.9           93
       1           51

and the code:

public class Historgram<T> 
{
    Dictionary<T, int> bins;
    public Historgram()
    {
        this.bins=new Dictionary<T, int>();
    }

    public void Add(T value)
    {
        if(bins.ContainsKey(value))
        {
            bins[value]++;
        }
        else
        {
            bins.Add(value, 1);
        }
    }
    public void Remove(T value)
    {
        if(bins.ContainsKey(value))
        {
            bins[value]--;
            if(bins[value]==0)
            {
                bins.Remove(value);
            }
        }
    }
    public int this[T x]
    {
        get
        {
            if(bins.ContainsKey(x))
            {
                return bins[x];
            }
            else
            {
                return 0;
            }
        }
        set
        {
            if(bins.ContainsKey(x))
            {
                bins[x]=value;
            }
            else
            {
                bins.Add(x, value);
            }
        }
    }
    public bool ContainsValue(T value) { return bins.ContainsKey(value); }
    public int Count { get { return bins.Count; } }
    public T[] Values { get { return bins.Keys.ToArray(); } }
    public int[] Quantities { get { return bins.Values.ToArray(); } }
}