【问题标题】:Conversion of Expression with nullable DateTime gives an error使用可为空的 DateTime 转换表达式会产生错误
【发布时间】:2015-11-16 12:10:11
【问题描述】:

在一个类中,我有一个静态方法通过提供一个表达式来获取 PropertyInfo。

public static PropertyInfo GetPropertyInfo<T>(Expression<Func<T, object>> expressie)
        {
            MemberExpression me;
            switch (expressie.Body.NodeType)
            {
                case ExpressionType.Convert:
                case ExpressionType.ConvertChecked:
                    var ue = expressie.Body as UnaryExpression;
                    me = ((ue != null) ? ue.Operand : null) as MemberExpression;
                    break;
                default:
                    me = expressie.Body as MemberExpression;
                    break;
            }
            if (me == null)
            {
                throw new InvalidOperationException("Expression does not refer to a property: " + expressie.ToString());
            }

            return (PropertyInfo)me.Member;
        }

为了使我的代码更安全,我更改了在调用此函数的部分方法中使用的表达式。

// Old
Expression<Func<T, Object>> expression;

// New (TProp instead of Object)
Expression<Func<T, TProp>> expression;

因此,我不能再使用旧的“GetPropertyInfo”方法,因为表达式与请求的参数不匹配。所以我创建了一个新的。

public static PropertyInfo GetPropertyInfo<T, TProp>(Expression<Func<T, TProp>> expressie)
        {
            MemberExpression me;
            switch (expressie.Body.NodeType)
            {
                case ExpressionType.Convert:
                case ExpressionType.ConvertChecked:
                    var ue = expressie.Body as UnaryExpression;
                    me = ((ue != null) ? ue.Operand : null) as MemberExpression;
                    break;
                default:
                    me = expressie.Body as MemberExpression;
                    break;
            }
            if (me == null)
            {
                throw new InvalidOperationException("Expression does not refer to a property: " + expressie.ToString());
            }

            return (PropertyInfo)me.Member;
        }

这个新表达式的主体与现有的完全相同。因为我想坚持 DRY 原则,所以我寻找了一种方法,在这两种情况下都使用一个方法体。这可以通过首先将 'Expression>' 转换为 'Expression>' 并将转换后的表达式传递给 'old' GetPropertyInfo(...) 方法来实现。我无法完全删除旧的“GetPropertyInfo”方法,因为其他代码仍然依赖它。

我使用的转换方法如下:

private static Expression<Func<T, object>> convertToObjectExpression<T, TProp>(Expression<Func<T, TProp>> expression)
        {
            return Expression.Lambda<Func<T,object>>(
                    Expression.Convert(expression.Body, typeof(object)),
                    expression.Parameters);
        }

这很好用,除非我使用带有可为空的 DateTime (DateTime?) 的表达式作为 TProp 类型。在这种情况下,以下行会导致 'me' 为空。

me = ((ue != null) ? ue.Operand : null) as MemberExpression;

有没有人知道是什么导致了这个问题?

我有一个包含问题的fiddle

【问题讨论】:

  • 在调试模式下,能不能看一下“expressie.Body”的类型?可能不是 UnaryExpression。
  • 这个函数怎么称呼?
  • “当我使用可以为空的 DateTime 的表达式时” - 你的意思是你将它与 DateTime? 类型的属性或 DateTime 类型的属性一起使用转换为 DateTime? .
  • @IvanStoev 这确实是从 DateTime 属性到 DateTime 的转换?输入表达式。我需要这个,因为在其他地方我重新使用 TProp 类型来定义另一个参数的类型,有时它必须可以提供 null(实际上是 'default(DateTime?)')。
  • @KemalKefeli 表达式主体是 UnaryExpression 类型,并且强制转换 ('var ue = expressie.Body as UnaryExpression') 也不会导致 ue 为空。

标签: c# linq lambda


【解决方案1】:

我建议你反其道而行之。不要从新的类型安全方法传递到旧方法(带有所有附加问题),而是从旧方法传递到新方法:

// relay old interface to new one for compatibility
public static PropertyInfo GetPropertyInfo<T>(Expression<Func<T, object>> expressie)
{
    return GetPropertyInfo<T, object>(expressie);
}

public static PropertyInfo GetPropertyInfo<T, TProp>(Expression<Func<T, TProp>> expressie)
{ /* your code, as you provided it */ }

如果您对此仍有疑问,请提供您的调用代码

编辑

因与问题无关而被删除

【讨论】:

  • 无论如何我都会投赞成票,但你的最后一次编辑真的毁了,否则一个很好的答案。您不需要类 instance 即可获取类元数据信息(如属性)。
  • @IvanStoev 是有道理的。我想我在自己的项目中考虑了很多,我主要关心的是具体对象的一些轻量级反射。
  • 我完全同意这个解决方案,但我将@IvanStoev 的答案标记为解决方案,因为他实际上解决了根本问题(重复转换)。
  • 他的回答可能确实对其他有类似问题的人更有帮助,所以对我来说完全没问题。
【解决方案2】:

有没有人知道是什么导致了这个问题?

您调用新函数的方式(使用DateTime? 从普通的DateTime 属性转换)和实现它的方式(通过convertToObjectExpression)导致有两个Convert 表达式,因此出现了问题。

为了修复它,请考虑用户 grek40 的建议,和/或按如下方式更改实现

public static PropertyInfo GetPropertyInfo<T>(Expression<Func<T, object>> expressie)
{
    var body = expressie.Body;
    while (body.NodeType == ExpressionType.Convert || body.NodeType == ExpressionType.ConvertChecked)
        body = ((UnaryExpression)body).Operand;
    var me = body as MemberExpression;
    var property = me != null ? me.Member as PropertyInfo : null;
    if (property == null)
    {
        throw new InvalidOperationException("Expression does not refer to a property: " + expressie.ToString());
    }
    return property;
}

【讨论】:

  • 这个答案解决了实际问题(重复转换)
猜你喜欢
  • 2015-01-10
  • 1970-01-01
  • 2015-10-10
  • 1970-01-01
  • 1970-01-01
  • 2018-06-17
  • 1970-01-01
  • 2014-09-11
  • 1970-01-01
相关资源
最近更新 更多