2012-10-24 52 views
6

我想创建一个通用列表<>,其类型在运行时声明。C#动态通用列表

我可以做以下事情,但由于它是动态的,我怀疑有速度的损失。我正在写一个外来数据库的包装,所以速度至关重要。

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

我读动态泛型类型this post,但不能让它工作。具体而言,该对象不会显示为List,因此不具有添加方法。

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); 

如何获得GDB显示为下列之一:

List<System.Int32> 
List<System.Int64> 
List<System.String> 
+1

它在这种情况下使用'List '可能会更容易。如果类型在编译时不知道,那么编译时检查泛型给你的帮助对你没有任何帮助。 – Servy

+0

不是一个坏主意,但我希望列表被键入,因为它将成为查询的一部分。 – IamIC

+0

列表怎么样? –

回答

5

一旦您使用了Activator.CreateInstance,您可以将结果转换为适当的类型。你可以使用IList例如:

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

注意上面抛出ArgumentException如果您要添加的类型不匹配的泛型类型。

+0

这是我正在寻找的答案。我投错了地方。 – IamIC

+0

如果您打算只使用非通用接口,那么您可以使用'List '并省去使用反射来创建列表的需要。 – Servy

+1

@Servy将要求每个int和long都被装箱和拆箱,这是一个重大的性能问题。 – IamIC

1

在你的情况,gdb将永远是一个System.Object,因为任何类型的CreateInstance回报的对象。

您在这里有几个选项 - 您可以将其设置为IList(非通用),因为您知道List<T>实现此接口,并使用IList.Add

或者,你可以只声明它dynamic,并使用动态绑定:

dynamic gdb = Activator.CreateInstance(specificListType); 

这将让你写你的代码,如果是适当类型的List<T>,而呼叫将在运行时绑定通过动态绑定。

3

哦,gdb的正确类型。由于您确定运行时的类型,因此编译器不知道它。因此,它的静态类型为object,并且不显示Add方法。您有几种选择来解决这个问题:

  1. 将它转换为正确的类型:

    switch (trail[dataPos].Type) 
    { 
        case GlobalsSubscriptTypes.Int32: 
         ((List<int>) gdb).Add(...); 
         break; 
        ... 
        default: 
         ((List<String>) gdb).Add(...); 
         break; 
    } 
    
  2. 转换为公用超类型:

    ((System.Collections.IList) gdb).Add(...); 
    
  3. 使用动态调用:

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

List<dynamic>编译与List<object>完全相同的类型,对类型List<int>没有速度损失,例如,超出了装箱值类型。但是如果你打算投给IList,你仍然会有FYI的拳击罚款。

可能会遇到麻烦的是调用Activator(反射方法),因为直接调用构造函数的速度可能会明显较慢。

有没有这件事?除非您实际运行配置文件,否则您将无法知晓,因为它总是取决于您的实际使用情况。

我最好的客人你真正想要做的是:

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; 
} 

此外,如果你真的需要做的操作,而不拳击做出一个通用的辅助方法来完成所有的工作:

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]); 
} 
+0

感谢您的明确解释。我喜欢助手的想法(尽管我没有多少考虑它的代码)。事实证明,由于多态性,我没有选择,只能在从数据库中获取值的类中使用动态。所以我假设我应该简单地使用名单。 – IamIC

+1

@IanC我会用任何会产生最简单代码的东西,然后再担心速度。 – jbtule