2016-09-20 5 views
3

Beim Konvertieren meiner .NET 4.5-Bibliothek in .NETStandard v1.6 lief ich in einem fehlgeschlagenen Komponententest, der zuvor bestanden hat.Unterschied in Expression.Subtract für DateTime zwischen .NET Core und .NET Framework

geortet ich das Problem auf die folgenden drei Codezeilen:

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

Dieser Ausdrucksbaum für .NET 4.5 kompiliert, sondern löst eine InvalidOperationException in .NETStandard v1.6:

Der Binäroperator Subtract nicht definiert für die Typen 'System.DateTime' und 'System.DateTime'.

jedoch für beide Ziele der folgende Code funktioniert:

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

Ich würde so die Ausdrucksbäume erwarten auch für .NET-Core zu kompilieren? Mache ich etwas falsch oder is this a bug in .NET Core?

+0

Ich habe gerade versucht mit konstanten Ausdrücken ('Expression.Constant (DateTime.Now)') für die das gleiche auftritt. –

+0

Von [ein kurzer Blick auf den Quellcode] (https://github.com/dotnet/corefx/blob/release/1.0.0-rc2/src/System.Linq.Expressions/src/System/Linq/Expressions/ BinaryExpression.cs # L758), 'GetUserDefinedBinaryOperator' scheint' null' zurückzugeben. –

Antwort

3

Es ist ein Fehler in System.Linq.Expressions Assembly.

Diese Methoden verwendet, um die Subtraktionsoperator Methode zu finden:

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

Wie Sie sehen, die GetAnyStaticMethod Picks zufällig die erste „op_Subtraction“ -Methode von DateTime, statt durch alle verfügbaren Looping, wo DateTime zwei hat solcher Operatormethoden:

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

so nimmt der Code die falsche, die dann nicht nur, weil Eingangstypen in DateTime und TimeSpan, nimmt nicht übereinstimmen.

In .NET 4.5 sie in der richtigen Weise suche nach Argumenttypen übergeben:

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); 
+0

Ehrfürchtig. Ihnen die Ehre [berichtet das genaue Problem auf dem GitHub-Projekt] (https://github.com/dotnet/corefx/issues/11907). :) Ich werde das Problem aktualisieren, um darauf hinzuweisen, dass Sie den genauen Grund dafür gefunden haben. –

+1

Oder Sie können Gabel und Beitrag zur Gemeinschaft :) –

+0

Ich könnte es später betrachten, wenn niemand, der tatsächliche Erfahrung mit diesem hat springt sofort auf. Zuerst möchte ich meine Bibliothek weiter migrieren (einschließlich eines Workarounds dafür). :) –

3

Dies ist in der Tat ein Fehler in der Implementierung von .NET-Core. Der Grund dafür ist, dass bestimmte APIs in .NET Core nicht verfügbar waren, als System.Linq.Expressions auf den Core portiert wurde. Daher wurde eine benutzerdefinierte Implementierung entwickelt, die nie abgefangen wurde.

Ich habe sent a PR to dotnet/corefx, um dies zu beheben. Für die Neugierigen war das Problem, dass die Methode, die den Operator findet, die Methoden durchläuft, aber aus der Schleife ausbricht, wenn sie eine Übereinstimmung findet, bevor sie überprüft, ob die Methode die gewünschte ist. Die Lösung besteht darin, die Parameterprüfung innerhalb der Schleife zu verschieben, z.

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

Nicht zu sicher, da ich nicht die vollständige Codebasis hier sehen kann, aber dies tut Überprüfen Sie auch den Rückgabetyp? Oder, ich nehme an, da Sie den Rückgabetyp nicht überlasten können, ist es nicht notwendig, dies zu überprüfen. :) Ich möchte nur überprüfen, ob die Pull-Anfrage alle Winkel abdeckt. Es könnte auch interessant sein, einige Unit-Tests hinzuzufügen, da dies bisher nicht aufgeklärt wurde. –

+0

Oh, ich sehe, du hast einen Komponententest hinzugefügt. Also sollte dieser Code wahrscheinlich auch DateTime - TimeSpan, ... korrekt abdecken? –

+1

Danke für einen Blick. Ich habe einen weiteren Komponententest hinzugefügt –

Verwandte Themen