2016-04-23 8 views
9

die folgenden Methoden in C# Bedenken Sie:Warum wird die Methode GetHashCode() für DateTime anders verglichen mit anderen Strukturen kompiliert?

public static int HashCodeFunction(Decimal value) 
{ 
    return value.GetHashCode(); 
} 
public static int HashCodeFunction(Int64 value) 
{ 
    return value.GetHashCode(); 
} 
public static int HashCodeFunction(DateTime value) 
{ 
    return value.GetHashCode(); 
} 

die sich den Anweisungen vom Compiler generiert Werfen wir einen Blick:

Für die Decimal Methode:

ldarga.s Parameter:System.Decimal value 
call Method:System.Decimal.GetHashCode() 
ret 

Für die Int64 Methode:

ldarga.s Parameter:System.Int64 value 
call Method:System.Int64.GetHashCode() 
ret 

Für die DateTime Methode:

ldarga.s Parameter:System.DateTime value 
constrained Type:System.DateTime 
callvirt Method:System.Object.GetHashCode() 
ret 

Warum ist die DateTime.GetHashCode() Methode als virtueller Aufruf von Object.GetHashCode() behandelt zu werden, wenn man bedenkt es eine außer Kraft gesetzt GetHashCode() Methode für die DateTime Struktur?

Des Weiteren kann ich eine Methode erstellen, die die System.DateTime.GetHashCode() Methode ohne den virtuellen Aufruf mit dem folgenden Code direkt aufruft:

DynamicMethod myDynamicMethod = new DynamicMethod("myHashCodeMethod", typeof(int), new[] { typeof(DateTime) }); 

ILGenerator gen = myDynamicMethod.GetILGenerator(); 
LocalBuilder local = gen.DeclareLocal(typeof(DateTime)); 
gen.Emit(OpCodes.Ldarga_S, local); 
gen.Emit(OpCodes.Call, typeof(DateTime).GetMethod("GetHashCode")); 
gen.Emit(OpCodes.Ret); 

dann einen Delegaten zu testen:

Func<DateTime, int> myNewHashCodeFunction = (Func<DateTime,int>)myDynamicMethod.CreateDelegate(typeof(Func<DateTime, int>)); 

DateTime dt = DateTime.Now; 

int myHashCode = myNewHashCodeFunction(dt); 
int theirHashCode = dt.GetHashCode(); 
// These values are the same. 

Nur neugierig Warum ist die Methode auf diese Weise standardmäßig für Int64 und Decimal implementiert, aber nicht DateTime.

+0

Außerordentliche Ansprüche erfordern außergewöhnliche Beweise, ich sehe keine. –

Antwort

6

Wenn Sie zu Roslyn kommen, beschreiben Sie ein altes Verhalten (Roslyn Version 1.1.0 und älter). Das neue Verhalten (Version 1.2.0 und neuer) ist auch call für DateTime zu verwenden.

Die Änderung wurde in pull request String concat with char and similar primitives should call overriden ToString directly (#7080) vorgenommen.

Das Problem mit der constrained.callvirtcall Optimierung ist, dass es bedeutet, das Entfernen der Überschreibung wird eine binäre brechende Änderung, so dass die Optimierung nicht universell angewendet werden kann. Es kann jedoch auf Typen angewendet werden, bei denen der Compiler sicher sein kann, dass die Überschreibung nicht entfernt wird.

Das alte Verhalten war, diese Optimierung für "intrinsische Typen" (diejenigen, die Schlüsselwörter in C# haben) und einige spezielle selten verwendete Typen zu verwenden. Das neue Verhalten besteht darin, die Optimierung für alle "speziellen Typen" zu verwenden, einschließlich der intrinsischen Typen und auch DateTime.

4

Ich habe Ihren Code auf meinem Computer getestet, alle drei Methoden geben call statt callvirt aus, also denke ich, dass dies compilerspezifisch sein könnte.

Meine Vermutung ist, frühere Version von Csc emittiert call nur für simple type virtuelle Methoden, so war es eigentlich diese einfachen Typen speziell gemacht, nicht DateTime. Später haben sie entschieden, dass es nichts wert ist, callvirt für Werttyp-Methodenaufrufe auszugeben, da sie niemals überschrieben würden. Daher werden alle Methodenaufrufe vom Werttyp mit call ausgegeben, während virtuelle Methodenaufrufe vom Referenztyp mit callvirt ausgeführt werden.

PS. Ich habe Visual Studio 2015 mit .NET Framework 4.6.1 auf meinem Computer. Ich habe mit .NET 2.0 bis 4.6.1 getestet, alle von ihnen erzeugt den gleichen IL-Code (keine callvirt für DateTime.GetHashCode).

+0

Ich bekomme das 'callvirt', wenn ich mit LINQPad 5 oder Microsoft.CodeAnalysis.CSharp 1.0.0 kompiliere (was zu sein scheint, was LINQPad verwendet). Dies scheint sich in Microsoft.CodeAnalysis.CSharp 1.2.0 geändert zu haben. – svick