1

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>.

Community
  • 1
  • 1
Girardi
  • 2,734
  • 3
  • 35
  • 50
  • 1
    @mbeckish No, it's not. You cannot use type inference in this situation to create the list the way you can for an anonymous type. – Servy Feb 05 '13 at 20:41
  • So I am wondering... why exactly would you want to load up a generic list of varying types at the same point in code. You will just have to differentiate or cast the list when consuming the list elsewhere, resulting in about the same amount of code as if you partitioned your lists separately to begin with. – David C Feb 05 '13 at 20:50
  • well, I used `List` as an example... My real problem is a `Histogram`, in which the bins may be integers or doubles; I know my life would be much easier if I just cast all values to doubles, but I wouldn't like to do that, as it could mess with the precision of integers... In fact, when I retrieve the Histogram, I won't need to cast to the input type, as I just retrieve the values of the bins in order to write them to a file... if you know what I mean... I use a lib by John Skeet to make operations between generic numerical types `T` – Girardi Feb 06 '13 at 01:32
  • I do not think it is 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 and, thus, create a generic `List` or any `SampleClass` based on that input data type! – Girardi Feb 06 '13 at 12:26
  • possible duplicate of [Pass An Instantiated System.Type as a Type Parameter for a Generic Class](http://stackoverflow.com/questions/266115/pass-an-instantiated-system-type-as-a-type-parameter-for-a-generic-class) – nawfal Jun 28 '14 at 16:39

5 Answers5

6

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>

Lee
  • 142,018
  • 20
  • 234
  • 287
  • That's a lot of headache for very little benefit. You're still going to need to either do a lot of reasoning at compile time to ensure that you never try to add the wrong type to a given list (to avoid an error) or do lots of checks at runtime to determine if you should. Yes, if you miss something you get an exception instead of a list with two different types in it, but it comes at a pretty steep cost. I don't really see it as being worth it. – Servy Feb 05 '13 at 20:41
  • @Servy - I don't think a couple of lines and a slight runtime cost is particularly big, although it's not up to me to decide whether it's worth it. – Lee Feb 05 '13 at 20:48
  • I'm not saying you shouldn't do it because it's inefficient, I'm saying you shouldn't do it because it's giving a false sense of security. It's contrary to the design of generics as a whole and is indicative of a greater underlying problem. – Servy Feb 05 '13 at 20:51
  • 3
    @Servy - It's typed as `IList` so it isn't giving any sense of security. If a collection should only contain one type of element, then it's preferable to use a generic list even if it isn't typed as one, so this is hardly 'contrary to the design of generics'. If this were Java, then I would agree there's no benefit. A runtime exception will be much easier to fix than a silent data error, which you could get using a `List`. – Lee Feb 05 '13 at 20:59
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.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • Well, the real problem behind this situation is that I'm creating an `Histogram`, in which `T` depends from the input file data (if the input data are integer, then `T` is `UInt32`, if the input data are double, then `T` is `Double`... What's your suggestion, then? – Girardi Feb 05 '13 at 20:37
  • @Girardi On the whole generics just aren't built for such situations. It's hard to try to suggest alternatives without knowing way more information than is appropriate to include in this question. – Servy Feb 05 '13 at 20:38
  • 1
    If your `Histogram` class actually changes its behavior depending on what type of data it has, then it isn't generic and shouldn't be defined as such. – Yuck Feb 05 '13 at 20:38
  • @Yuck It does not change... It is just generic so it can accept Double or UInt32 – Girardi Feb 05 '13 at 20:39
2

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);
Community
  • 1
  • 1
adt
  • 4,320
  • 5
  • 35
  • 54
  • So what's the advantage of using this over a `List` or an `ArrayList`? You can't actually leverage the fact that the list is generic. – Servy Feb 05 '13 at 20:39
  • 1
    @Servy I read your answer and its quite right for this situation. since Op knows what will be type in compile time its ok. But question is also relevant to MakeGenericType and creating generic types on runtime. I thought it might be a good suggestion and helpful. – adt Feb 05 '13 at 20:41
  • It's an example of answering the question in a way that doesn't actually solve the problem. I consider that a bad idea. – Servy Feb 05 '13 at 20:42
  • No, it doesn't solve the problem, it just answers the literal question that's asked. The entire point of using generics is to get *compile time* verification that you aren't trying to add/remove/search/get items in the collection that are not of the appropriate type. Since you've cast the list to a non-generic version you lose all of that. You get a runtime check, but that's of very little benefit. Doing this is contrary to the design of generics and is only going to cause more and more problems elsewhere in the program unless the underlying problem(s) are addressed. – Servy Feb 05 '13 at 20:47
  • I understand your point. Op wants to know how to create a generic class based on code provided . Of course this could be done without generics. He also use ArrayList List etc. You are free to edit my our your answer. – adt Feb 05 '13 at 20:51
  • You're demonstrating a failure to look beyond the literal words of the question that's asked and see the underlying problem that the OP faces, so while the answer may answer the question, it's not actually *helpful*. It's more likely to in fact be *harmful* to the OP. That's my entire point. – Servy Feb 05 '13 at 20:54
2
Type t = Program.CheckType(someInput);
var l = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(t)); 
kaques
  • 41
  • 5
2

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(); } }
}
John Alexiou
  • 28,472
  • 11
  • 77
  • 133
  • what do you mean with "trying to hold both types in the same variable"? – Girardi Feb 06 '13 at 01:34
  • With generics `List` and `List` are considered different types. The only thing they have in common is implementing `IDictionary` and deriving from `object`. – John Alexiou Feb 06 '13 at 13:31