Как я могу создать экземпляр произвольного типа Array во время выполнения? - PullRequest
27 голосов
/ 30 декабря 2008

Я пытаюсь десериализовать массив неизвестного типа во время компиляции. Во время выполнения я обнаружил тип, но не знаю, как создать экземпляр.

Что-то вроде:

Object o = Activator.CreateInstance(type);

, который не работает, потому что нет конструктора без параметров, Array, похоже, не имеет никакого конструктора.

Ответы [ 4 ]

53 голосов
/ 30 декабря 2008

Использовать Array.CreateInstance .

12 голосов
/ 30 декабря 2008

Вы можете использовать одну из перегрузок CreateInstance массива, например, :-

object o = Array.CreateInstance(type, 10);
3 голосов
/ 03 июля 2016

Альтернативой является использование деревьев выражений для производительности. Например, если у вас есть массив тип , 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() на type, если вы передаете сам тип элемента.

3 голосов
/ 28 декабря 2015

Довольно старый пост, но пока он отвечает на новый вопрос, хотя и публикует связанный пример создания многомерного массива.

Принимая тип (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 ] ]
...