如何使用Expression构建匿名类型?

时间:2022-09-15 19:15:48

In C# 3.0 you can use Expression to create a class with the following syntax:

在C#3.0中,您可以使用Expression创建具有以下语法的类:

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

But how do you use Expression to create an Anonymous class?

但是如何使用Expression创建Anonymous类?

//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();

3 个解决方案

#1


18  

You're close, but you have to be aware that anonymous types don't have default constructors. The following code prints { Name = def, Num = 456 }:

你很接近,但你必须要知道匿名类型没有默认构造函数。以下代码打印{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);

If you don't have to create many instances of this type, Activator.CreateInstance will do just as well (it's faster for a few instances, but slower for many). This code prints { Name = ghi, Num = 789 }:

如果你不必创建这种类型的许多实例,Activator.CreateInstance也会这样做(对于一些实例来说速度更快,但对于许多实例来说速度更慢)。此代码打印{Name = ghi,Num = 789}:

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

#2


6  

Since an anonymous type doesn't have a default empty constructor, you cannot use the Expression.New(Type) overload ... you have to provide the ConstructorInfo and parameters to the Expression.New method. To do that, you have to be able to get the Type ... so you need to make a "stub" instance of the anonymous type, and use that to get the Type, and the ConstructorInfo, and then pass the parameters to the Expression.New method.

由于匿名类型没有默认的空构造函数,因此不能使用Expression.New(Type)重载...您必须向Expression.New方法提供ConstructorInfo和参数。要做到这一点,你必须能够获得Type ...所以你需要创建一个匿名类型的“存根”实例,并使用它来获取Type和ConstructorInfo,然后将参数传递给Expression.New方法。

Like this:

喜欢这个:

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();

#3


4  

You can avoid using DynamicInvoke which is painfully slow. You could make use of type inference in C# to get your anonymous type instantiated generically. Something like:

您可以避免使用速度非常慢的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();
}

Now you can call,

现在你可以打电话,

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.

You could use the AnonymousInstantiator method to generate functions to instantiate any anonymous type with any number of properties, just that you have to pass an appropriate example first. The input parameters have to be passed as an object array. If you worry boxing performance there then you have to write a custom instantiator which accepts just string and int as input parameters, but the use of such an instantiator will be a bit more limited.

您可以使用AnonymousInstantiator方法生成函数,以实例化具有任意数量属性的任何匿名类型,只需要首先传递适当的示例。输入参数必须作为对象数组传递。如果你担心拳击性能那么你必须编写一个自定义实例化器,它只接受string和int作为输入参数,但是使用这样的实例化器会有点受限。

#1


18  

You're close, but you have to be aware that anonymous types don't have default constructors. The following code prints { Name = def, Num = 456 }:

你很接近,但你必须要知道匿名类型没有默认构造函数。以下代码打印{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);

If you don't have to create many instances of this type, Activator.CreateInstance will do just as well (it's faster for a few instances, but slower for many). This code prints { Name = ghi, Num = 789 }:

如果你不必创建这种类型的许多实例,Activator.CreateInstance也会这样做(对于一些实例来说速度更快,但对于许多实例来说速度更慢)。此代码打印{Name = ghi,Num = 789}:

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

#2


6  

Since an anonymous type doesn't have a default empty constructor, you cannot use the Expression.New(Type) overload ... you have to provide the ConstructorInfo and parameters to the Expression.New method. To do that, you have to be able to get the Type ... so you need to make a "stub" instance of the anonymous type, and use that to get the Type, and the ConstructorInfo, and then pass the parameters to the Expression.New method.

由于匿名类型没有默认的空构造函数,因此不能使用Expression.New(Type)重载...您必须向Expression.New方法提供ConstructorInfo和参数。要做到这一点,你必须能够获得Type ...所以你需要创建一个匿名类型的“存根”实例,并使用它来获取Type和ConstructorInfo,然后将参数传递给Expression.New方法。

Like this:

喜欢这个:

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();

#3


4  

You can avoid using DynamicInvoke which is painfully slow. You could make use of type inference in C# to get your anonymous type instantiated generically. Something like:

您可以避免使用速度非常慢的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();
}

Now you can call,

现在你可以打电话,

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.

You could use the AnonymousInstantiator method to generate functions to instantiate any anonymous type with any number of properties, just that you have to pass an appropriate example first. The input parameters have to be passed as an object array. If you worry boxing performance there then you have to write a custom instantiator which accepts just string and int as input parameters, but the use of such an instantiator will be a bit more limited.

您可以使用AnonymousInstantiator方法生成函数,以实例化具有任意数量属性的任何匿名类型,只需要首先传递适当的示例。输入参数必须作为对象数组传递。如果你担心拳击性能那么你必须编写一个自定义实例化器,它只接受string和int作为输入参数,但是使用这样的实例化器会有点受限。