Expression.LessThan vs LambdaExpression> - PullRequest
       22

Expression.LessThan vs LambdaExpression>

1 голос
/ 25 февраля 2011

У меня есть выражение LambdaExpression, которое принимает один объект в качестве параметра и в конечном итоге возвращает объект. Для целей тестирования, вот Lambda (созданная от руки, чтобы соответствовать тому, что я действительно передаю), которая возвращает DateTime, упакованный в виде объекта. В целях проработки этого, LambdaExpression принимает XmlNode и возвращает объект. Он должен возвращать объект, реальный тип возврата может быть любым из: DateTime, bool, int, decimal, XmlDocument [пока что] Общая идея заключается в том, что где-то глубоко в синтаксическом анализаторе эта лямбда создается, и она извлекает значение из своего входного параметра и возвращает его типизированным, но помещенным в квадрат в объекте.

     XmlNode node = null;
       ParameterExpression instanceExpression = Expression.Parameter(typeof(DynamicNode), "instance");
       ParameterExpression result = Expression.Parameter(typeof(object), "result");
       LabelTarget blockReturnLabel = Expression.Label(typeof(object));
       BlockExpression block = Expression.Block(
                             typeof(object),
                             new[] {  result },
                             Expression.Assign(result, Expression.Convert(Expression.Constant(DateTime.Now.AddSeconds(-1)), typeof(object))),
                             Expression.Return(blockReturnLabel, result),
                             Expression.Label(blockReturnLabel, Expression.Constant(-2, typeof(object))));
LambdaExpression lax = Expression.Lambda<Func<XmlNode, object>>(block, instanceExpression);

Позже в коде мы оцениваем <, <=,>,> =, == и! =, Поэтому мы хотим сравнить результат этого LambdaExpression с другим выражением

Обычно мы можем предположить, что LambdaExpression находится слева от Expression.LessThan справа может быть любое выражение, но давайте предположим, что оно напечатано. Это означает, что это может быть ConstantExpression или подобное ... но у него есть тип.

Это означает, что Expression.LessThan [например] завершается неудачей, потому что LambdaExpression при вызове Expression.Invoke возвращает объект, а RHS - это любой тип этого типа.

Предполагая, что тип, помещенный в рамку внутри объекта, возвращенного из LambdaExpression, фактически сопоставим с типом в правой части; например,

(object)5 < 6

Как мне написать выражение, которое может сравнивать упакованный тип с распакованным без сбоя? Я пробовал различные перестановки в linqpad, в том числе пытался написать это в обычном c # - то есть без выражений, просто вложенный if-then-else, однако я не мог заставить это работать совершенно правильно. Обычно я бы написал что-то вроде этого:

/*
int i = 3;
object o = (object)i;
int compare = 4;
*/
DateTime dt = DateTime.Now;
object o = (object)dt;
DateTime compare = DateTime.Now.AddSeconds(1);

bool b = false;
if(o.GetType().IsAssignableFrom(compare.GetType()))
{
    if(o is int)
    {
        b = (int)o < (int)(object)compare;
    }
    if(o is DateTime)
    {
        b = (DateTime)o < (DateTime)(object)compare;
    }
    if(o is decimal)
    {
        b = (decimal)o < (decimal)(object)compare;
    }
}
Console.WriteLine(b);

При этом, предполагая, что o и сравнивают на самом деле один и тот же тип и один из них помечен как объект, мы все равно можем выполнить <операцию ... </p>

Поэтому я предполагаю, что мой вопрос заключается в том, как мне написать приведенный выше код, когда у меня есть LambdaExpression слева и Expression справа [если два типа не совпадают, false в результате лучше, чем сбой ]

Надеюсь, кто-то может помочь,

Gareth

Ответы [ 2 ]

1 голос
/ 25 февраля 2011

Объединение моего исходного поста с вашими ответами парней - похоже, это работает в LinqPad

XmlNode node = null;
ParameterExpression instanceExpression = Expression.Parameter(typeof(XmlNode), "instance");
ParameterExpression result = Expression.Parameter(typeof(object), "result");
LabelTarget blockReturnLabel = Expression.Label(typeof(object));
BlockExpression block = Expression.Block(
                     typeof(object),
                     new[] {  result },
                     //this would normally be a function invoke
                     Expression.Assign(result, Expression.Convert(Expression.Constant(DateTime.Now.AddSeconds(-1)), typeof(object))),
                     Expression.Return(blockReturnLabel, result),
                     Expression.Label(blockReturnLabel, Expression.Constant(-2, typeof(object))));
LambdaExpression lax = Expression.Lambda<Func<XmlNode, object>>(block, instanceExpression);

var left = Expression.Invoke(lax, instanceExpression);
//false result
//var right = Expression.Constant(5, typeof(int));  
//true result
var right = Expression.Constant(DateTime.Now, typeof(DateTime));

var unboxedLeft = Expression.Unbox(left, right.Type);  
var lessThanEx = Expression.LessThan(unboxedLeft, right);     

var body = Expression.Condition(Expression.TypeEqual(left, right.Type),  lessThanEx,  Expression.Constant(false));  
var expression = Expression.Lambda<Func<XmlNode, bool>>(body, instanceExpression); 

bool b = expression.Compile()(node); 
Console.WriteLine(b);
1 голос
/ 25 февраля 2011

Как мне написать выражение, которое может сравните упакованный тип с распакованным типа без сбоев?

Для этого можно использовать метод Expression.Unbox: «Создает выражение UnaryExpression, представляющее явную распаковку».

Давайте рассмотрим пример (int)(object)5 < 6:

// boxed int
var left = Expression.Constant(5, typeof(object));

// int
var right = Expression.Constant(6, typeof(int));

// More generally, you can use right.Type  instead of typeof(int)
// if its static type is appropriate. 
// Otherwise, you may need to unbox it too.
var unboxedLeft = Expression.Unbox(left, typeof(int));

var lessThanEx = Expression.LessThan(unboxedLeft, right);    
var expression = Expression.Lambda<Func<bool>>(lessThanEx, null);

// True : (int)(object)5 < 6
bool b = expression.Compile()();

Так что я думаю, мой вопрос, как мне напишите приведенный выше код, когда у меня есть LambdaExpression слева и Выражение справа. [если два типа не совпадают, false в результате лучше> сбоя]

В этом случае вы можете написать условное выражение, которое проверяет, совпадает ли тип времени выполнения в штучной упаковке с типом правой части, и выполнять сравнение без распаковки + меньше, если они или просто возвращают false в противном случае.

1022 * Е.Г. *

// From earlier
var left =  ...
var right = ...
var lessThanEx = ...

var body = Expression.Condition(Expression.TypeEqual(left, right.Type), 
                                lessThanEx, 
                                Expression.Constant(false));

var expression = Expression.Lambda<Func<bool>>(body, null);
...