Reputation: 18259
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>
Upvotes: 7
Views: 6013
Reputation: 31809
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]);
}
Upvotes: 1
Reputation: 172408
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:
Cast it to the correct type:
switch (trail[dataPos].Type)
{
case GlobalsSubscriptTypes.Int32:
((List<int>) gdb).Add(...);
break;
...
default:
((List<String>) gdb).Add(...);
break;
}
Cast to a common supertype:
((System.Collections.IList) gdb).Add(...);
Use dynamic invocation:
dynamic gdb = Activator.CreateInstance(specificListType);
gdb.Add(...);
Upvotes: 3
Reputation: 102793
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.
Upvotes: 5
Reputation: 564751
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.
Upvotes: 1