2016-04-23 35 views
9

Considerate i seguenti metodi in C#:Perché il metodo GetHashCode() è compilato in modo diverso per DateTime rispetto ad altre strutture?

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

Guardiamo le istruzioni generate dal compilatore:

Per il metodo Decimal:

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

Per il metodo Int64:

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

Per il metodo DateTime:

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

Perché il metodo DateTime.GetHashCode() essere trattata come una chiamata virtuale di Object.GetHashCode(), considerando che c'è un GetHashCode() metodo ignorato per il DateTime struct?

Inoltre, posso creare un metodo che chiama direttamente il metodo System.DateTime.GetHashCode() senza la chiamata virtuale utilizzando il seguente codice:

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

quindi creare un delegato per testarlo:

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. 

Solo curioso perché il metodo è implementato in questo modo per impostazione predefinita per Int64 e Decimal, ma non DateTime.

+0

affermazioni straordinarie richiedono prove straordinarie, non vedo alcuna. –

risposta

6

Quando si tratta di Roslyn, ciò che si descrive è un vecchio comportamento (Roslyn versione 1.1.0 e precedenti). Il nuovo comportamento (versione 1.2.0 e successive) consiste nell'utilizzare call per DateTime.

La modifica è stata apportata in pull request String concat with char and similar primitives should call overriden ToString directly (#7080).

Il problema con l'ottimizzazione constrained.callvirtcall è che la rimozione dell'override diventa una modifica di interruzione binaria, quindi l'ottimizzazione non può essere applicata universalmente. Ma può essere applicato a tipi in cui il compilatore può essere sicuro che l'override non verrà rimosso.

Il vecchio comportamento era quello di utilizzare questa ottimizzazione per "tipi intrinseche" (quelli che hanno le parole chiave in C#) e alcuni tipi raramente utilizzati speciali. Il nuovo comportamento è quello di utilizzare l'ottimizzazione per tutti i "tipi speciali", che comprendono i tipi intrinseci ed anche DateTime.

4

Ho testato il codice sulla mia macchina, tutti e tre i metodi emettono call anziché callvirt, quindi penso che questo potrebbe essere specifico del compilatore.

La mia ipotesi è che la versione precedente di Csc emette call solo per i metodi virtuali simple type, quindi in realtà erano questi tipi semplici resi speciali, non DateTime. Successivamente, hanno deciso che non valeva nulla per emettere callvirt per le chiamate al metodo del tipo valore, poiché non sarebbero mai state sovrascritte. Quindi tutte le chiamate al metodo di tipo value vengono emesse con call, mentre il metodo di riferimento chiama il metodo virtuale con callvirt.

PS. Ho Visual Studio 2015 con .NET Framework 4.6.1 sul mio computer. Ho provato con .NET 2.0 a 4.6.1, tutti loro generano lo stesso codice IL (no callvirt per DateTime.GetHashCode).

+0

ottengo il 'callvirt' Quando compilo con LINQPad 5 o Microsoft.CodeAnalysis.CSharp 1.0.0 (che sembra essere ciò che LINQPad sta usando). Sembra che sia stato modificato in Microsoft.CodeAnalysis.CSharp 1.2.0. – svick