Как использовать Expression для создания анонимного типа? - PullRequest
22 голосов
/ 18 сентября 2010

В C # 3.0 вы можете использовать Expression для создания класса со следующим синтаксисом:

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

Ответы [ 3 ]

21 голосов
/ 18 сентября 2010

Вы близки, но вы должны знать, что у анонимных типов нет конструкторов по умолчанию. Следующий код печатает { 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);
6 голосов
/ 18 сентября 2010

Поскольку анонимный тип не имеет пустого конструктора по умолчанию, вы не можете использовать перегрузку 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();
4 голосов
/ 19 августа 2015

Вы можете избежать использования 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 в качестве входных параметров, но использование такого экземпляра будет немного более ограниченным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...