代码之家  ›  专栏  ›  技术社区  ›  CrashCodes

如何在运行时创建任意Array类型的实例?

  •  36
  • CrashCodes  · 技术社区  · 17 年前

    我试图在编译时反序列化一个未知类型的数组。在运行时,我发现了该类型,但我不知道如何创建实例。

    类似于:

    Object o = Activator.CreateInstance(type);
    

    这不起作用,因为没有无参数构造函数,Array似乎没有任何构造函数。

    4 回复  |  直到 13 年前
        1
  •  63
  •   Jon Skeet    17 年前
        2
  •  12
  •   AnthonyWJones    17 年前

    您可以使用Array的CreateInstance重载之一,例如:-

    object o = Array.CreateInstance(type, 10);
    
        3
  •  5
  •   Arghya C    10 年前

    这是一篇相当老的帖子,但在回答一个新问题的同时,发布了一个创建多维数组的相关示例。

    假设类型( elementType )as int 例如二维阵列。

    var size = new[] { 2, 3 };                
    var arr = Array.CreateInstance(typeof(int), size);
    

    例如,当它是二维的时,它可以填充为

    var value = 1;
    for (int i = 0; i < size[0]; i++)
        for (int j = 0; j < size[1]; j++)
            arr.SetValue(value++, new[] { i, j });
    //arr = [ [ 1, 2, 3 ], [ 4, 5, 6 ] ]
    
        4
  •  4
  •   nawfal Donny V.    10 年前

    另一种方法是使用表达式树来提高性能。例如,如果你有数组 类型 , type 你可以的

    var ctor = type.GetConstructors().First(); // or find suitable constructor
    var argsExpr = ctor.GetParameters().Select(x => Expression.Constant(0)); 
    var func = Expression.Lambda<Func<object>>(Expression.New(ctor, argsExpr)).Compile();
    

    这只返回一个空数组。可能不是很有用。MSDN状态 GetConstructors 不保证任何顺序,因此您可能需要一个逻辑来找到具有正确参数的正确构造函数,以便以正确的大小实例化。例如,您可以这样做:

    static Func<object> ArrayCreateInstance(Type type, params int[] bounds) // can be generic too
    {
        var ctor = type
            .GetConstructors()
            .OrderBy(x => x.GetParameters().Length) // find constructor with least parameters
            .First();
    
        var argsExpr = bounds.Select(x => Expression.Constant(x)); // set size
        return Expression.Lambda<Func<object>>(Expression.New(ctor, argsExpr)).Compile();
    }
    

    使用以下方法可以更容易地实现同样的目标 Expression.NewArrayBounds 而不是 Expression.New ,如果你得到的只是数组元素类型,而不是数组类型本身,那么它就更有效了。演示:

    static Func<object> ArrayCreateInstance(Type type, params int[] bounds) // can be generic too
    {
        var argsExpr = bounds.Select(x => Expression.Constant(x)); // set size
        var newExpr = Expression.NewArrayBounds(type.GetElementType(), argsExpr);
        return Expression.Lambda<Func<object>>(newExpr).Compile();
    }
    
    // this exercise is pointless if you dont save the compiled delegate, but for demo purpose:
    
    x = string[] {...
    y = ArrayCreateInstance(x.GetType(), 10)(); // you get 1-d array with size 10
    
    x = string[,,] {...
    y = ArrayCreateInstance(x.GetType(), 10, 2, 3)(); // you get 3-d array like string[10, 2, 3]
    
    x = string[][] {...
    y = ArrayCreateInstance(x.GetType(), 10)(); // you get jagged array like string[10][]
    

    只要改变 type.GetElementType() 简单来说 类型 如果你传递的是元素类型本身。

    推荐文章