How to use Expression to build an Anonymous Type?

前端 未结 3 618
一生所求
一生所求 2020-12-03 03:00

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

var exp = Expression.New(typeof(MyClass));
var lambda = LambdaExpression.Lambda         


        
相关标签:
3条回答
  • 2020-12-03 03:24

    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 }:

    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 }:

    Type anonType = new { Name = "abc", Num = 123 }.GetType();
    object myObj = Activator.CreateInstance(anonType, "ghi", 789);
    Console.WriteLine(myObj);
    
    0 讨论(0)
  • 2020-12-03 03:26

    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:

    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.

    0 讨论(0)
  • 2020-12-03 03:34

    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.

    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();
    
    0 讨论(0)
提交回复
热议问题