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

36
我正在尝试反序列化一个在编译时未知类型的数组。在运行时,我已经发现了该类型,但我不知道如何创建一个实例。
类似这样:
Object o = Activator.CreateInstance(type);

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

4个回答

67

2
稍有问题。如果我知道类型是System.String[],并使用Array.CreateInstance(),那么得到的将是System.String[][]。 - CrashCodes
3
是的,您需要传递 元素 类型,而不是最终数组类型。 - Jon Skeet

12

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

object o = Array.CreateInstance(type, 10);

5

这是一篇比较旧的文章,但是在回答一个新问题时,想到了发布一个相关的创建多维数组的例子。

假设类型(elementType)为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

另一种提高性能的方法是使用表达式树。例如,如果您有数组类型类型,可以这样做:

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()更改为简单的type

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接