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

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

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

但是如何使用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(); 

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

由于匿名类型没有默认的空构造函数,因此不能使用Expression.New(Type)重载…您必须向Expression.New方法提供ConstructorInfo和参数。 要做到这一点,你必须能够获得Type …所以你需要创建一个匿名类型的“存根”实例,并使用它来获取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 AnonymousInstantiator(T example) { var ctor = typeof(T).GetConstructors().First(); var paramExpr = Expression.Parameter(typeof(object[])); return Expression.Lambda> ( 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作为输入参数,但是使用这样的实例化器会有点受限。