Expression.LessThan vs LambdaExpression <func <paramtype,object>&gt; </func <paramtype,object>

时间:2011-02-25 11:10:07

标签: c# types expression-trees unboxing

我有一个LambdaExpression,它将一个对象作为参数并最终返​​回一个对象。 出于测试目的,这里是一个Lambda(创建了一个与我真正传入的匹配的longhand),它返回一个封装为对象的DateTime。 为了解决这个问题,LambdaExpression采用XmlNode并返回一个对象。 它必须返回一个对象,真正的返回类型可以是以下任何一个:DateTime,bool,int,decimal,XmlDocument [到目前为止] 一般的想法是在解析器深处的某个地方创建这个lambda,它从它的输入参数中提取一个值并返回它的类型,但是在一个对象中装箱。

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

稍后在代码中,我们正在评估&lt ;,&lt; =,&gt;,&gt; =,==和!=所以我们要将此LambdaExpression的结果与另一个Expression

进行比较

通常,我们可以假设LambdaExpression位于Expression.LessThan的左侧 在右边,几乎可以是任何表达,但我们假设它是键入的。 这意味着它可能是ConstantExpression或类似的......但它有一个类型。

这意味着Expression.LessThan [例如]失败,因为在调用Expression.Invoke时LambdaExpression返回一个对象而RHS就是那个类型。

假设从LambdaExpression返回的对象内部装箱的类型实际上与右侧的类型相当; e.g。

(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和compare实际上是相同的类型,并且其中一个被装箱作为对象,我们仍然可以执行&lt;操作...

所以我想我的问题是,当我左边有一个LambdaExpression时,如何编写上面的代码,右边的Expression如果[如果这两个不是同一类型,那么结果是假的比崩溃更好]

希望有人能提供帮助,

加雷

2 个答案:

答案 0 :(得分:1)

  

如何编写可以的表达式   将盒装类型与未装箱的类型进行比较   类型没有崩溃?

您可以使用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在左边,和   表达在右边。 [如果两者不是同一类型,那么结果是假的更好&gt;而不是崩溃]

在这种情况下,您可以编写一个条件表达式来检查盒装对象的运行时类型是否与右侧的类型相同,并执行拆箱+小于比较的情况。他们是,或者只是返回假。

E.g。

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

答案 1 :(得分:1)

将我原来的帖子与你们的答案结合起来 - 这似乎在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);