.NET Core 和 .NET Framework 之间 DateTime 的 Expression.Subtract 差异

Difference in Expression.Subtract for DateTime between .NET Core and .NET Framework

在将我的 .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

The binary operator Subtract is not defined for the types 'System.DateTime' and 'System.DateTime'.

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

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

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

这是 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);

这确实是.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;
    }