【问题标题】:Difference in Expression.Subtract for DateTime between .NET Core and .NET Framework.NET Core 和 .NET Framework 之间 DateTime 的 Expression.Subtract 差异
【发布时间】:2016-09-20 18:20:59
【问题描述】:

在将我的 .NET 4.5 库转换为 .NETStandard v1.6 时,我遇到了以前通过的单元测试失败。

我将问题定位在以下三行代码:

ParameterExpression arg1 = Expression.Parameter( typeof( DateTime ), "arg1" );
ParameterExpression arg2 = Expression.Parameter( typeof( DateTime ), "arg2" );
var test = Expression.Subtract( arg1, arg2 );

此表达式树编译为.NET 4.5,但在.NETStandard v1.6 中抛出InvalidOperationException

没有为类型定义二元运算符 Subtract 'System.DateTime' 和 'System.DateTime'。

但是,对于这两个目标,以下代码都有效:

DateTime one = new DateTime();
DateTime two = new DateTime();
TimeSpan difference = one - two;

因此我希望表达式树也可以为 .NET Core 编译?我做错了什么,还是is this a bug in .NET Core

【问题讨论】:

  • 我刚刚尝试过使用常量表达式 (Expression.Constant( DateTime.Now )),同样的情况。
  • 来自a quick glance at the source codeGetUserDefinedBinaryOperator 似乎正在返回null

标签: c# expression-trees .net-core .net-core-rc2


【解决方案1】:

这是System.Linq.Expressions 程序集中的一个错误。

这些方法用于查找 Subtract 运算符方法:

public static MethodInfo GetAnyStaticMethodValidated(this Type type, string name, Type[] types)
{
    // Method name is "op_Subtraction" in your case
    MethodInfo anyStaticMethod = type.GetAnyStaticMethod(name);
    // DateTime and DateTime in your case
    if (!anyStaticMethod.MatchesArgumentTypes(types))
    {
        return null;
    }
    return anyStaticMethod;
}

public static MethodInfo GetAnyStaticMethod(this Type type, string name)
{
    foreach (MethodInfo current in type.GetRuntimeMethods())
    {
        if (current.IsStatic && current.Name == name)
        {
            return current;
        }
    }
    return null;
}

如您所见,GetAnyStaticMethodDateTime 中随机选择第一个“op_Subtraction”方法,而不是循环遍历所有可用方法,其中DateTime 有两个这样的运算符方法:

public static DateTime operator -(DateTime d, TimeSpan t);
public static TimeSpan operator -(DateTime d1, DateTime d2);

因此,代码选择了错误的 DateTimeTimeSpan,然后因为输入类型不匹配而失败。

在 .NET 4.5 中,它们通过传递参数类型以正确的方式进行搜索:

Type[] types = new Type[]
{
    leftType, // DateTime in your case
    rightType // DateTime in your case
};
BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
// Method name is "op_Subtraction" in your case
MethodInfo methodInfo = nonNullableType.GetMethodValidated(name, bindingAttr, null, types, null);

【讨论】:

  • 太棒了。给你reporting the exact issue on the github project的荣誉。 :) 我会更新这个问题,指出你找到了它背后的确切原因。
  • 如果没有人对此有实际经验,我可能会在以后考虑它。首先,我想继续迁移我的库(包括为此找到解决方法)。 :)
  • 解决方法很简单:自己找到正确的运算符方法,并将第三个参数传递给重载的Expression.Subtract 方法
【解决方案2】:

这确实是 .NET Core 实现中的一个 bug。原因是当 System.Linq.Expressions 移植到核心时,某些 API 在 .NET Core 中不可用,因此开发了自定义实现,但从未被捕获。

我已经sent a PR to dotnet/corefx 来解决这个问题。 对于好奇的人来说,问题在于找到运算符的方法会循环遍历这些方法,但在找到匹配项时会跳出循环,然后再检查该方法是否是我们想要的方法。解决方法是在循环内移动参数检查,例如

        internal static MethodInfo GetAnyStaticMethodValidated(
        this Type type,
        string name,
        Type[] types)
    {
        foreach (var method in type.GetRuntimeMethods())
        {
            if (method.IsStatic && method.Name == name && method.MatchesArgumentTypes(types))
            {
                return method;
            }
        }
        return null;
    }

【讨论】:

  • 不太确定,因为我在这里看不到完整的代码库,但这是否也验证了返回类型?或者,我想因为你不能重载返回类型,所以不需要检查这个。 :) 只是想仔细检查一下拉取请求是否涵盖所有角度。为此添加一些单元测试可能也很有趣,因为这之前没有被捕获。
  • 哦,我看到你确实添加了一个单元测试。所以这段代码可能还应该涵盖 DateTime - TimeSpan, ... 对吗?
  • 感谢您的浏览。我添加了另一个单元测试
猜你喜欢
  • 1970-01-01
  • 2021-02-20
  • 1970-01-01
  • 2020-05-15
  • 2017-08-15
  • 2021-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多