C# 如何使用表达式构建匿名类型?

C# 如何使用表达式构建匿名类型?,c#,linq,expression-trees,anonymous-types,C#,Linq,Expression Trees,Anonymous Types,在C#3.0中,可以使用表达式创建具有以下语法的类: var exp = Expression.New(typeof(MyClass)); var lambda = LambdaExpression.Lambda(exp); object myObj = lambda.Compile().DynamicInvoke(); 但如何使用表达式创建匿名类呢 //anonymousType = typeof(new{ Name="abc", Num=123}); Type anonymousType

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

var exp = Expression.New(typeof(MyClass));
var lambda = LambdaExpression.Lambda(exp);
object myObj = lambda.Compile().DynamicInvoke();
但如何使用表达式创建匿名类呢

//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();
//anonymousType=typeof(新的{Name=“abc”,Num=123});

Type anonymousType=表达式。NewAnonymousType 由于匿名类型没有默认的空构造函数,因此不能使用
表达式.New(type)
重载。。。您必须为
表达式.New
方法提供
ConstructorInfo
和参数。要做到这一点,你必须能够得到类型。。。因此,您需要创建匿名类型的“存根”实例,并使用该实例获取
类型
,以及
构造函数信息
,然后将参数传递给
表达式.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();

很接近,但必须注意匿名类型没有默认构造函数。以下代码打印
{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);
Type anonType = new { Name = "abc", Num = 123 }.GetType();
object myObj = Activator.CreateInstance(anonType, "ghi", 789);
Console.WriteLine(myObj);
如果您不必创建许多这种类型的实例,
Activator.CreateInstance
也可以(对于一些实例来说速度更快,但对于许多实例来说速度较慢)。此代码打印
{Name=ghi,Num=789}

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

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

可能的dup of:@Flash,这是不可能的,至少不能直接使用。当您创建匿名类型时,编译器为您带来了很多“魔力”——它是真正声明一个具有一系列属性的真正C#类的语法糖。编译器只是为您完成所有这些。没有一种表达式树类型能够自动完成所有这一切。如果您查看我引用的链接,它提供了一个解决方法。但是,它使用Reflection.Emit,这不是为了假装。Kirk:OP想要构造一个匿名类,而不是从头开始创建一个。只要他在编译时知道属性的名称和类型是什么,他就可以让编译器为他创建类型,他所要做的就是找出如何对其进行实例化。@Gabe,我不同意你对OP想要什么的解释,但我想我们会看到的。。)@加布,我不同意。他注释掉了他对类的定义,大概是为了找到一种通过表达式树实现这一点的方法。此外,这篇文章的标题是“如何使用表达式构建匿名类型?”我从来没有用动词“build”来指代“实例化”。这是一个聪明的解决方案。但通常需要使用表达式树(API)编写某些东西的原因正是因为在编译时没有这些信息。如果有人这样做,他们首先会使用普通的C#表达式。@Kirk OPs代码不敢苟同。在很多情况下,您知道类型,但仍然需要构建ExpressionTree。DynamicIQ-2-Sql for oneJust挑剔,如果匿名类型是
new{}
:),则匿名类型具有空构造函数,但类型anonType=new{Name=“abc”,Num=123}.GetType()@Flash:如果您认为C#code
new{Name=“abc”,Num=123}
在LINQ表达式中使用时,会在运行时创建一个新类型,那么您就错了。编译器在编译时创建类型,生成的表达式树与使用非匿名类型的表达式树无法区分。Flash:您想要动态匿名类型吗?你打算用它们做什么?我个人更喜欢使用
typeanontype=new{Name=default(string),Num=default(int)}.GetType()
当我发现定义匿名类型时,可以更清楚地将定义与使用分开。通常,您会将包含此代码的方法设置为泛型,以便它的T类型参数之一是匿名对象。然后可以使用
typeof(T).GetType()
而不是在方法体中内联
new{…}