表达差异.减去.net Core和.net Framework之间的DateTime

本文关键字:net Framework 之间 DateTime Core 减去 | 更新日期: 2023-09-27 18:08:01

在将我的.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的系统。DateTime'和'System.DateTime'.

但是,对于两个目标,下面的代码都可以工作:
DateTime one = new DateTime();
DateTime two = new DateTime();
TimeSpan difference = one - two;

我因此会期望表达式树编译为。net核心以及?是我做错了什么,还是这是一个错误在。net核心?

表达差异.减去.net Core和.net Framework之间的DateTime

这是System.Linq.Expressions汇编中的一个错误。

这些方法用于查找减法操作符方法:

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;
}

如您所见,GetAnyStaticMethod随机从DateTime中选择第一个"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被移植到Core时,某些api在。net Core中是不可用的,所以开发了一个自定义实现,而这从未被捕获。

我已经发送PR到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;
    }