1

我有一个 LambdaExpression,它将一个对象作为参数并最终返​​回一个对象。出于测试的目的,这里有一个 Lambda(创建以匹配我真正传入的内容),它返回一个作为对象装箱的 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);

稍后在代码中,我们正在评估 <、<=、>、>=、== 和 !=,因此我们希望将此 LambdaExpression 的结果与另一个 Expression 进行比较

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

这意味着 Expression.LessThan [例如] 失败,因为在调用 Expression.Invoke 时 LambdaExpression 返回一个对象,而 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 和 compare 实际上是相同的类型,并且其中一个被装箱为对象,我们仍然可以执行 < 操作...

所以我想我的问题是,当我在左边有一个 LambdaExpression 和在右边的 Expression 时,我该如何编写上面的代码[如果两者不是同一类型,则结果为 false 比崩溃要好]

希望有人能帮忙,

加雷斯

4

2 回答 2

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,在右边有一个 Expression 时,我该如何编写上面的代码。[如果两者不是同一类型,则结果为 false 比崩溃好>]

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

例如

// 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);
于 2011-02-25T11:15:17.157 回答
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);
于 2011-02-25T11:55:31.157 回答