7

I want to create a generic List<> whose type is declared at runtime.

I can do the following, but since it's dynamic, I suspect there is a speed penalty. I'm writing a wrapper to an exotic database, so speed is critical.

List<dynamic> gdb = new List<dynamic>()

I read this post in dynamic generic types, but can't get it to work. Specifically, the object is not appearing as a List and hence has no add method.

    Type ac;

    switch (trail[dataPos].Type)
    {
        case GlobalsSubscriptTypes.Int32:
            ac = typeof(System.Int32);

            break;
        case GlobalsSubscriptTypes.Int64:
            ac = typeof(System.Int64);

            break;

        default:
            ac = typeof(System.String);

            break;
    }

    var genericListType = typeof(List<>);
    var specificListType = genericListType.MakeGenericType(ac);
    var gdb = Activator.CreateInstance(specificListType);

How do I get gdb to appear as one of the following:

List<System.Int32>
List<System.Int64>
List<System.String>
9
  • 1
    It might be easier to just use a List<object> in that case. If the type isn't known at compile time then the compile time checking that generics give you won't be of any help to you. Commented Oct 24, 2012 at 16:02
  • Not a bad idea, but I want the list to be typed since it will be part of a query. Commented Oct 24, 2012 at 16:03
  • @IanC Why is it that your database objects aren't known at compile time. Really, that seems to be the underlying problem here. And as I said before, having the List be typed won't do you any good if you don't know the type at compile time. You won't be able to cast the result to a typed list, you won't know the types that you can or can't add at compile time, and you won't know anything about what the objects in the list can do, so you can't do anything useful with the items when you take them out. Commented Oct 24, 2012 at 16:04
  • 2
    @IanC so say you have a List<dynamic> d and List<object> o. Then proceed to assign int i = d[0]; and int j = (int)o[0]; if the lists contain ints then j assignment is certainly faster, if the lists contain something implicitly convertible to ints 'j' assignment would not work at all and 'i' would. Commented Oct 24, 2012 at 17:14
  • 1
    @jbtule I tested this yesterday, and yes, object wins. Commented Oct 25, 2012 at 18:22

4 Answers 4

5

Once you've used Activator.CreateInstance, you can cast the result to the appropriate type. You could use IList for example:

var gdb = (IList)Activator.CreateInstance(specificListType);
gdb.Add(1);

Note that the above throws an ArgumentException if the type you're adding does not match the generic type.

Sign up to request clarification or add additional context in comments.

6 Comments

This is the answer I was looking for. I was casting in the wrong place.
If you're going to just use the non-generic interface you may as well just use a List<object> and save yourself the need to use reflection to create the List.
@Servy that will require every int and long to be boxed and unboxed, which is a significant performance hit.
@IanC Using IList will still cause the ints to be boxed and unboxed when you add in this method...
@IanC That's going to be happening anyway here, since you're using the non-generic methods. The add method called here is public void Add(object item).
|
3

Oh, gdb is of the correct type. Since you determine the type at runtime, the compiler does not know it. Hence, it is statically typed as object and does not show an Add method. You have a few options to fix that:

  1. Cast it to the correct type:

    switch (trail[dataPos].Type) 
    { 
        case GlobalsSubscriptTypes.Int32: 
            ((List<int>) gdb).Add(...); 
            break; 
        ...
        default: 
            ((List<String>) gdb).Add(...); 
            break; 
    } 
    
  2. Cast to a common supertype:

    ((System.Collections.IList) gdb).Add(...);
    
  3. Use dynamic invocation:

    dynamic gdb = Activator.CreateInstance(specificListType);
    gdb.Add(...);
    

Comments

1

In your case, gdb will always be a System.Object, as CreateInstance returns objects of any type.

You have a few options here - you could cast it as IList (non-generic), as you know that List<T> implements this interface, and use IList.Add.

Alternatively, you could just declare it dynamic, and use dynamic binding:

dynamic gdb = Activator.CreateInstance(specificListType);

This will let you write your code as if it's a List<T> of the appropriate type, and the calls will be bound at runtime via dynamic binding.

Comments

1

List<dynamic> compiles the exact same type as List<object> no speed penalties over a typed List<int>, for example, beyond boxing value types. However if you are going to cast to IList you are still going to have the boxing penalty FYI.

You could run into trouble is calling Activator (a reflection method) as it is likely to be dramatically slower that calling a constructor directly.

Does any of this matter? You won't know unless you actually run profile because it will always depends on your actual usage.

My best guest what you really want to do is:

IList gdb;

switch (trail[dataPos].Type)
{
    case GlobalsSubscriptTypes.Int32:
        gdb = new List<int>();
        break;
    case GlobalsSubscriptTypes.Int64:
        gdb = new List<long>();
        break;
    default:
        gdb = new List<string>();
        break;
}

Also if you really need to do operations without boxing make a generic helper method to do all your work:

switch (trail[dataPos].Type)
{
    case GlobalsSubscriptTypes.Int32:
        return Helper<int>(trail[dataPos]);
    case GlobalsSubscriptTypes.Int64:
        return Helper<long>(trail[dataPos]);
    default:
        return Helper<string>(trail[dataPos]);
}

2 Comments

Thanks for your clear explanation. I like the Helper idea (although I haven't thought much about its code). As it turns out, due to polymorphism I have no option but to use dynamic in the class that gets the value from the database. So I am assuming I should simply go with List<dynamic>.
@IanC I'd go with whatever will produce the simplest code, and then worry about speed later.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.