为什么有些对象属性是UnaryExpression而另一些是MemberExpression?

85

根据我在选择使用lambda而不是字符串属性名来选择模型属性问题的回答,想要将属性添加到集合中,如下所示:

var props = new ExportPropertyInfoCollection<JobCard>();
props.Include(model => model.BusinessInstallNumber).Title("Install No").Width(64).KeepZeroPadding(true);
props.Include(model => model.DeviceName).Title("Device").Width(70);
props.Include(model => model.DateRequested).Title("Request Date").Format("{0:dd/MM/yyyy}").Width(83);

我在Include方法中编写了以下代码:

public class PropertyCollection<T>
{
    public void Include(Expression<Func<T, object>> expression)
    {
        var memberExpression = expression.Body as MemberExpression;
        if (memberExpression != null)
        {
            var pes = new ExportPropertyInfoBuilder {Property = new ExportPropertyInfo {Property = memberExpression.Member as PropertyInfo}};
            Properties.Add(pes.Property.Property.Name, pes.Property);
            return pes;
    }

然而,在运行代码时,我发现一些lambda表达式按预期产生了MemberExpression值,但其他的却产生了UnaryExpression值。在添加所有属性之前,我必须将第一行代码更改为以下内容:

var memberExpression = expression.Body as MemberExpression ?? ((UnaryExpression) expression.Body).Operand as MemberExpression;

所有的属性都是“简单”类型,即在POCO业务对象中为字符串、DateTime、整数、布尔等。它们带有几个不同的DataAnnotations属性。
导致我的示例中的一些lambda产生MemberExpression值,而其他的则产生UnaryExpression值是什么原因?在我的示例中,第一个UnaryExpression位于第三行的DateTime属性上,但布尔属性也会产生UnaryExpressions。

1
UnaryExpression表达式是否可能出现在可空的存在(或“不存在”)情况下? - leppie
@leppie,我怀疑它是在非空列上。在我的示例中,第一个UnaryExpression是在DateTime上,而之前的MemberExpressions是在字符串上。随后的UnaryExpression是在bool上。 - ProfK
我会稍微调查一下。我以前使用(或者说滥用)过这样的表达式,从来没有出现过问题,这意味着它总是一个“MemberExpression”,否则我的代码就会失败。你正在运行哪个版本的.NET?我还没有使用.NET 4。 - leppie
不确定是否有影响,但您能否包括 Include 期望的表达式类型(包括约束条件)? - leppie
@leppie,我已经添加了方法'header',并尽我所记得的最好地设置了表达式参数。我现在手头没有它。 - ProfK
1
类型成员的表达式导致不同的表达式 - 参见https://dev59.com/sGcs5IYBdhLWcg3wSSDH - Jay Wick
1个回答

72

我知道问题出在哪里了。你的表达式返回类型为object

如果你将其更改为Expression<Func<T, R>>,则应该可以正确推断返回类型,并且不会发生UnaryExpression(我假设这是一些装箱操作)。

更新:

Include的签名应该是:

public void Include<T, R>(Expression<Func<T, R>> expression)

1
请原谅我在这里的愚蠢,但是R应该是什么?我不能将其设置为MemberExpression,因为MemberExression在表达式的Body属性中。我同意UnaryExpression可能是由于装箱而引起的。 - ProfK
4
@ProfK:R 是通过推断得出的,它将是返回属性的类型。你可能不会使用它,但也许会用到它 :) - leppie
2
我想我知道正在发生什么事情。因为表达式的返回类型是对象,所以它被装箱了。使用有类型的返回类型,这种情况就不会发生。谢谢@leppie!相信函数式编程者能在这里提供帮助 :-) - ProfK
1
很好的回答。Include的签名不应该是public void Include<R>(Expression<Func<T, R>> expression)吗?因为T已经被PropertyCollection类定义了。 - Xcalibur
@Xcalibur:不,你必须指定(或定义)使用的所有通用类型参数。 - leppie
3
@leppie,T 泛型类型参数已在类级别上定义,因此,如果意图不同,则应使用不同的类型参数名称,否则它是多余的。 - Xcalibur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接