C # конструировать лямбда с использованием Expression.Call не нравится определенные типы в качестве параметров? - PullRequest
2 голосов
/ 27 января 2011

По разным причинам я динамически создаю C # лямбду, используя возможности дерева выражений.например, я могу сделать Funcво время выполнения, как показано в следующем фрагменте.

   public static bool myMethod( object obj ) {  … }

    // Construct a Func<string,bool>
    var myMethod = GetType().GetMethod("myMethod");
    var lambdaParams = new ParameterExpression[] {Expression.Parameter(typeof (string))};
    var callMyMethod = Expression.Call(myMethod, lambdaParams);
    var lambda = Expression.Lambda(typeof(Func<string,bool>), callMyMethod, lambdaParams);
    var del = (Func<string,bool>)lambda.Compile();
    del("foo"); // works

Однако, если я использую тот же код, чтобы попытаться сделать Funcили Funcон взрывается там, где указано со следующим странным исключением:

    // Construct a Func<DateTime,bool> or perhaps a struct type fails... why?
    var myMethod = GetType().GetMethod("myMethod");
    var lambdaParams = new ParameterExpression[] {Expression.Parameter(typeof (DateTime))};
    var callMyMethod = Expression.Call(myMethod, lambdaParams);  // Blows up here…

System.ArgumentException: Expression of type 'System.DateTime' cannot be used for parameter of type 'System.Object' of method 'Boolean myMethod(System.Object)'

Итак, строка работает, а List работает, но int32 не работает и DateTime.Что здесь происходит?Я не знаю, как работают глубокие внутренности C #, но я предполагаю, что это из-за того, что int действительно обрабатывается как примитив и, возможно, DateTime (будучи структурой) ...

Любая помощь в этомс благодарностью.

спасибо, Пэт

Ответы [ 2 ]

2 голосов
/ 27 января 2011

Насколько я понимаю, Expression.Call не выполняет автобокс аргументов типа значения. Я не могу найти никакой документации на этот счет, но она упоминается на странице этого форума .

Одним из обходных путей было бы явное преобразование бокса в выражении с Expression.TypeAs.

Создает UnaryExpression, которое представляет явную ссылку или преобразование бокса, где ноль предоставляется в случае сбоя преобразования.

В вашем случае это должно работать:

var boxedParams = lambdaParams.Select(p => Expression.TypeAs(p, typeof(object)))
                              .ToArray();

var callMyMethod = Expression.Call(myMethod, boxedParams);

(Вам не нужны причудливые лямбды, если есть только один параметр)

В зависимости от реального использования вам, возможно, придется проверить, необходимо ли преобразование бокса, в зависимости от того, являются ли рассматриваемые типы (типы) значениями типа (типов).

1 голос
/ 27 января 2011

Проверьте это: вы должны указать DateTime, поскольку DateTime не является ссылочным типом!

// Construct a Func<DateTime,bool>
var myMethod = typeof(Program).GetMethod("myMethod");
var param = Expression.Parameter(typeof(DateTime));
var boxy = Expression.TypeAs(param, typeof(object));

var callMyMethod = Expression.Call(myMethod, boxy);
var lambda = Expression.Lambda(typeof(Func<DateTime, bool>), callMyMethod, new ParameterExpression[] { param });
var del = (Func<DateTime,bool>)lambda.Compile();
del(DateTime.Now); // works
...