我正在尝试创建以下形式的表达式:
e => e.CreationDate;
CreationDate
的类型是long
,但是我希望表达式返回一个object
。
我想使用object
作为返回类型,因为表达式是在运行时基于查询参数动态构建的。查询参数指定要在表达式中访问的属性,例如:
> entities?order=creationDate
> entities?order=score
正如您所看到的,我可以根据不同类型的不同属性排序,因此返回类型object
将允许我构建尽可能泛型的表达式。
问题是,当我尝试创建表达式:
ParameterExpression entityParameter = Expression.Parameter(typeof(Entity), "e");
Expression propertyAccess = Expression.Property(entityParameter, property);
Expression<Func<Entity, object>> result = Expression.Lambda<Func<Entity, object>>(propertyAccess, entityParameter);
我得到以下异常:
System类型的表达式。Int64'不能用作返回类型的系统。对象'
这很奇怪,因为据我所知,所有类型都从object
扩展(似乎多态性尚未被表达式树支持)。
然而,我在网上搜索,偶然发现了这个类似的问题:
System类型的表达式。Int32'不能用于返回类型'System '。对象'
根据Jon Skeet的回答,我将最后一行修改为:
Expression<Func<Entity, object>> result = Expression.Lambda<Func<Entity, object>>(Expression.Convert(propertyAccess, typeof(object)), entityParameter);
这工作得很好,但它不能生成我想要的表达式。相反,它生成如下内容:
e => Convert(e.CreationDate)
我不能使用这个解决方案,因为在程序后面,如果表达式体不是MemberExpression
(即成员访问操作),就会抛出异常
我一直在网上寻找一个满意的答案,但没有找到。
返回类型为object
的e => e.CreationDate
如何实现?
根据您使用result
的方式,您可以使用委托类型Func<Entity, long>
动态创建它,并将其键入LambdaExpression
:
ParameterExpression entityParameter = Expression.Parameter(typeof(Entity), "e");
Expression propertyAccess = Expression.Property(entityParameter, property);
var funcType = typeof(Func<,>).MakeGenericType(typeof(Entity), property.PropertyType);
LambdaExpression result = Expression.Lambda(funcType, propertyAccess, entityParameter);
简短回答:不,这是不可能的。值类型需要装箱才能被视为对象。编译器通常会为你做,但如果你自己构建代码(例如表达式树),你需要将其指定为显式转换,就像你在找到的答案中看到的那样。如果您不能将其作为非泛型LambdaExpression,那么我会在您期望MemberExpression的地方额外处理转换情况,或者使用PropertyInfo,并仅在最后一刻构造order Expression。