web-dev-qa-db-ja.com

実行時に任意の配列タイプのインスタンスを作成するにはどうすればよいですか?

コンパイル時に不明なタイプの配列を逆シリアル化しようとしています。実行時にタイプを発見しましたが、インスタンスを作成する方法がわかりません。

何かのようなもの:

Object o = Activator.CreateInstance(type);

パラメータのないコンストラクタがないため機能しません。Arrayにはコンストラクタがないようです。

30
CrashCodes

Array.CreateInstance を使用します。

56
Jon Skeet

配列のCreateInstanceオーバーロードの1つを使用できます。例:-

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

別の方法は、パフォーマンスのために式ツリーを使用することです。例:配列がある場合typetypeできます

_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
nawfal

かなり古い投稿ですが、新しい質問に答えている間、多次元配列を作成する関連する例を投稿しています。

たとえば、タイプ(elementType)をintとし、2次元配列を想定します。

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

たとえば、2次元の場合、次のように入力できます。

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 ] ]
3
Arghya C