在C#中考虑以下方法:
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();
}
让我们看一下编译器生成的指令:
对于Decimal
方法:
ldarga.s Parameter:System.Decimal value
call Method:System.Decimal.GetHashCode()
ret
对于Int64
方法:
ldarga.s Parameter:System.Int64 value
call Method:System.Int64.GetHashCode()
ret
对于DateTime
方法:
ldarga.s Parameter:System.DateTime value
constrained Type:System.DateTime
callvirt Method:System.Object.GetHashCode()
ret
为什么DateTime.GetHashCode()
方法被视为Object.GetHashCode()
的虚拟调用,考虑到GetHashCode()
结构有一个被覆盖的DateTime
方法?
此外,我可以使用以下代码创建一个直接调用System.DateTime.GetHashCode()
方法而无需虚拟调用的方法:
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);
然后创建一个委托来测试它:
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.
很好奇为什么默认情况下Int64
和Decimal
采用这种方法实现该方法,而不是DateTime
。
答案 0 :(得分:7)
说到Roslyn,你所描述的是旧行为(Roslyn版本1.1.0及更早版本)。新行为(版本1.2.0及更高版本)也将call
用于DateTime
。
constrained.callvirt
→call
优化的问题在于,它意味着删除覆盖变为二进制分解更改,因此优化无法普遍应用。但它可以应用于编译器可以确保不会删除覆盖的类型。
旧的行为是将这种优化用于&#34;内在类型&#34; (在C#中有关键字的那些)和一些特殊的很少使用的类型。新的行为是对所有&#34;特殊类型&#34;使用优化,其中包括内在类型和DateTime
。
答案 1 :(得分:4)
我在我的机器上测试了你的代码,所有三种方法都发出call
而不是callvirt
,所以我想这可能是编译器特有的。
我的猜测是,早期版本的Csc仅为simple type个虚拟方法发出call
,所以实际上这些简单类型是特殊的,而不是DateTime
。后来,他们认为为值类型方法调用发出callvirt
是没有价值的,因为它们永远不会被覆盖。因此,所有值类型方法调用都使用call
发出,而引用类型虚拟方法调用使用callvirt
。
PS。我在我的机器上安装了带有.NET Framework 4.6.1的Visual Studio 2015。我使用.NET 2.0到4.6.1进行了测试,所有这些都生成了相同的IL代码(callvirt
没有DateTime.GetHashCode
)。