Come posso creare un'istanza di un tipo di array arbitrario in fase di esecuzione?
-
03-07-2019 - |
Domanda
Sto cercando di deserializzare un array di un tipo sconosciuto al momento della compilazione. In fase di esecuzione ho scoperto il tipo, ma non so come creare un'istanza.
Qualcosa del tipo:
Object o = Activator.CreateInstance(type);
che non funziona perché non esiste un costruttore senza parametri, Array non sembra avere alcun costruttore.
Soluzione
Usa Array.CreateInstance .
Altri suggerimenti
È possibile utilizzare uno dei sovraccarichi CreateInstance di Array, ad esempio :-
object o = Array.CreateInstance(type, 10);
Abbastanza un vecchio post, ma pur rispondendo a una nuova domanda, sebbene di pubblicare un esempio correlato di creazione di un array multidimensionale.
Supponendo il tipo ( elementType
) come int
e, ad esempio, un array bidimensionale.
var size = new[] { 2, 3 };
var arr = Array.CreateInstance(typeof(int), size);
Quando è bidimensionale, ad esempio, può essere popolato come
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 ] ]
Un'alternativa è usare gli alberi delle espressioni per le prestazioni. Ad es. se hai l'array type , type
puoi farlo
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();
Questo restituisce solo un array vuoto. Probabilmente non molto utile. MSDN afferma che GetConstructors
non garantisce alcun ordine, quindi potresti aver bisogno di una logica per trovare il costruttore giusto con i parametri giusti per creare un'istanza con le dimensioni corrette. Ad es. potresti fare:
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();
}
Lo stesso può essere ottenuto molto più facilmente con Expression.NewArrayBounds
invece di Expression.New
, più funziona se tutto ciò che ottieni è il tipo di elemento array, non il tipo di array si. Demo:
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][]
Basta cambiare type.GetElementType ()
semplicemente type
se quello che stai passando è il tipo di elemento stesso.