[英]Difference in Expression.Subtract for DateTime between .NET Core and .NET Framework
While converting my .NET 4.5
library to .NETStandard v1.6
I ran into a failing unit test which used to pass before. 在将我的
.NET 4.5
库转换为.NETStandard v1.6
我遇到了一个以前通过的失败单元测试。
I pinpointed the problem to the following three lines of code: 我将问题确定为以下三行代码:
ParameterExpression arg1 = Expression.Parameter( typeof( DateTime ), "arg1" );
ParameterExpression arg2 = Expression.Parameter( typeof( DateTime ), "arg2" );
var test = Expression.Subtract( arg1, arg2 );
This expression tree compiles for .NET 4.5
, but throws an InvalidOperationException
in .NETStandard v1.6
: 此表达式树为
.NET 4.5
编译,但在.NETStandard v1.6
抛出InvalidOperationException
:
The binary operator Subtract is not defined for the types 'System.DateTime' and 'System.DateTime'.
没有为类型'System.DateTime'和'System.DateTime'定义二元运算符Subtract。
However, for both targets the following code works: 但是,对于这两个目标,以下代码有效:
DateTime one = new DateTime();
DateTime two = new DateTime();
TimeSpan difference = one - two;
I thus would expect the expression trees to compile for .NET Core as well? 因此,我希望表达式树也可以为.NET Core编译? Am I doing something wrong, or is this a bug in .NET Core ?
我做错了什么,或者这是.NET Core中的错误 ?
It's a bug in System.Linq.Expressions
assembly. 这是
System.Linq.Expressions
程序集中的一个错误。
These methods are used to find the Subtract operator method: 这些方法用于查找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;
}
As you see, the GetAnyStaticMethod
picks randomly the first "op_Subtraction" method from DateTime
, instead of looping through all available, where DateTime
has two of such operator methods: 如您所见,
GetAnyStaticMethod
从DateTime
随机选取第一个“op_Subtraction”方法,而不是循环遍历所有可用的方法,其中DateTime
有两个这样的运算符方法:
public static DateTime operator -(DateTime d, TimeSpan t);
public static TimeSpan operator -(DateTime d1, DateTime d2);
So the code picks the wrong one that takes in DateTime
and TimeSpan
, then just fails because input types don't match. 所以代码选择了错误的一个接受
DateTime
和TimeSpan
,然后失败因为输入类型不匹配。
In .NET 4.5 they do search in a proper way by passing argument types: 在.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);
This is indeed a bug in the implementation of .NET Core. 这确实是.NET Core实现中的一个错误。 The reason is that certain APIs were not avaiable in .NET Core when System.Linq.Expressions was ported to core, so a custom implementation was developed and this was never caught.
原因是当System.Linq.Expressions被移植到核心时,某些API在.NET Core中是不可用的,因此开发了一个自定义实现,并且从未捕获过。
I've sent a PR to dotnet/corefx to fix this. 我已经发送了一个PR到dotnet / corefx来解决这个问题。 For the curious, the problem was that the method finding the operator loops through the methods, but breaks out of the loop when it finds a match, before checking that the method is the one we want.
对于好奇的问题是,找到运算符的方法循环遍历方法,但在找到匹配之前突破循环,然后检查方法是否是我们想要的方法。 The fix is to move parameter checking inside the loop, eg
修复是在循环内移动参数检查,例如
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;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.