在 C# 3.0 中,您可以使用表达式创建具有以下语法的类:
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();
你已经很接近了,但你必须知道匿名类型没有默认构造函数。以下代码打印
{ 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);
您可以避免使用
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
方法生成函数来实例化具有任意数量属性的任何匿名类型,只是您必须首先传递一个适当的示例。输入参数必须作为对象数组传递。如果您担心那里的拳击性能,那么您必须编写一个自定义实例化器,它只接受 string
和 int
作为输入参数,但这样的实例化器的使用会受到更多限制。
由于匿名类型没有默认的空构造函数,因此您不能使用
Expression.New(Type)
重载...您必须向 ConstructorInfo
方法提供 Expression.New
和参数。为此,您必须能够获取类型...因此您需要创建匿名类型的“存根”实例,并使用它来获取 Type
和 ConstructorInfo
,然后通过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();
使用动态类型创建:
using DynamicAnonymousType; // nuget
var dynamicType = DynamicFactory.CreateType(
("Name", typeof(string)),
("Num", typeof(int)),
);
var initExpr = Expression.MemberInit(
Expression.New(dynamicType),
new [] {
Expression.Bind(dynamicType.GetProperty("Name"), Expression.Constant("example")),
Expression.Bind(dynamicType.GetProperty("Num"), Expression.Constant(123)),
}
);