web-dev-qa-db-ja.com

式を使用して匿名型を作成するにはどうすればよいですか?

C#3.0では、Expressionを使用して、次の構文でクラスを作成できます。

var exp = Expression.New(typeof(MyClass));
var lambda = LambdaExpression.Lambda(exp);
object myObj = lambda.Compile().DynamicInvoke();

しかし、Expressionを使用して匿名クラスを作成するにはどうすればよいでしょうか。

//anonymousType = typeof(new{ Name="abc", Num=123});
Type anonymousType = Expression.NewAnonymousType???  <--How to do ?
var exp = Expression.New(anonymousType);
var lambda = LambdaExpression.Lambda(exp);
object myObj = lambda.Compile().DynamicInvoke();
24
Flash

あなたは近くにいますが、匿名型にはデフォルトのコンストラクターがないことに注意する必要があります。次のコードは{ Name = def, Num = 456 }を出力します。

Type anonType = new { Name = "abc", Num = 123 }.GetType();
var exp = Expression.New(
            anonType.GetConstructor(new[] { typeof(string), typeof(int) }),
            Expression.Constant("def"),
            Expression.Constant(456));
var lambda = LambdaExpression.Lambda(exp);
object myObj = lambda.Compile().DynamicInvoke();
Console.WriteLine(myObj);

このタイプのインスタンスを多数作成する必要がない場合は、Activator.CreateInstanceも同様に機能します(一部のインスタンスでは高速ですが、多くのインスタンスでは低速です)。このコードは{ Name = ghi, Num = 789 }を出力します:

Type anonType = new { Name = "abc", Num = 123 }.GetType();
object myObj = Activator.CreateInstance(anonType, "ghi", 789);
Console.WriteLine(myObj);
21
Gabe

匿名型にはデフォルトの空のコンストラクターがないため、Expression.New(Type)オーバーロードを使用できません...ConstructorInfoとパラメーターをExpression.Newメソッドに指定する必要があります。これを行うには、型を取得できる必要があります...したがって、匿名型の「スタブ」インスタンスを作成し、それを使用してTypeConstructorInfo、次にパラメータをExpression.Newメソッドに渡します。

このような:

var exp = Expression.New(new { Name = "", Num = 0 }.GetType().GetConstructors()[0], 
                         Expression.Constant("abc", typeof(string)), 
                         Expression.Constant(123, typeof(int)));
var lambda = LambdaExpression.Lambda(exp);
object myObj = lambda.Compile().DynamicInvoke();

非常に遅いDynamicInvokeの使用を避けることができます。 C#で型推論を利用して、匿名型を一般的にインスタンス化することができます。何かのようなもの:

public static Func<object[], T> AnonymousInstantiator<T>(T example)
{
    var ctor = typeof(T).GetConstructors().First();
    var paramExpr = Expression.Parameter(typeof(object[]));
    return Expression.Lambda<Func<object[], T>>
    (
        Expression.New
        (
            ctor,
            ctor.GetParameters().Select
            (
                (x, i) => Expression.Convert
                (
                    Expression.ArrayIndex(paramExpr, Expression.Constant(i)),
                    x.ParameterType
                )
            )
        ), paramExpr).Compile();
}

今、あなたは呼び出すことができます、

var instantiator = AnonymousInstantiator(new { Name = default(string), Num = default(int) });

var a1 = instantiator(new object[] { "abc", 123 }); // strongly typed
var a2 = instantiator(new object[] { "xyz", 789 }); // strongly typed
// etc.

AnonymousInstantiatorメソッドを使用して、任意の数のプロパティを持つ任意の匿名型をインスタンス化する関数を生成できます。最初に適切な例を渡す必要があります。入力パラメータは、オブジェクト配列として渡す必要があります。そこでボクシングのパフォーマンスが心配な場合は、入力パラメーターとしてstringintだけを受け入れるカスタムインスタンス化ツールを作成する必要がありますが、そのようなインスタンス化ツールの使用は少し制限されます。

4
nawfal