最近在分析会话期间,一种方法引起了我的注意,在剖析器的反编译版中看起来像这样:
public static double dec2f(Decimal value)
{
if (value == new Decimal(-1, -1, -1, true, (byte) 0))
return double.MinValue;
try
{
return (double) value;
}
catch
{
return double.MinValue;
}
}
这是多年前编写的遗留代码的一部分,根据分析器(处于采样模式),这种方法浪费了太多时间。在我看来,这是因为try-catch阻止了内联,我花了一些时间来刷新关于十进制到双转换技巧的记忆。确保此转换无法抛出后,我删除了try-catch块。
但是当我再次查看 source 代码的简化版本时,我想知道为什么反编译版本显示奇怪的Decimal构造函数,而它是简单的Decimal.MinValue用法:
public static double dec2f(decimal value)
{
if (value == DecimalMinValue)
{
return Double.MinValue;
}
return (double)value;
}
首先,我认为它是一个反编译器的bug,但幸运的是它还显示了IL版本的方法:
public static double dec2f(Decimal value)
{
if (value == new Decimal(-1, -1, -1, true, (byte) 0))
.maxstack 6
.locals init (
[0] float64 V_0
)
IL_0000: ldarg.0 // 'value'
IL_0001: ldc.i4.m1
IL_0002: ldc.i4.m1
IL_0003: ldc.i4.m1
IL_0004: ldc.i4.1
IL_0005: ldc.i4.0
IL_0006: newobj instance void [mscorlib]System.Decimal::.ctor(int32, int32, int32, bool, unsigned int8)
IL_000b: call bool [mscorlib]System.Decimal::op_Equality(valuetype [mscorlib]System.Decimal, valuetype [mscorlib]System.Decimal)
IL_0010: brfalse.s IL_001c
return double.MinValue;
好吧,看起来每次使用“常量”时都会涉及到Decimal构造函数。喜欢Decimal.MinValue!所以下一个问题出现了 - 这个构造函数内部是什么,使用Decimal.MinValue和定义本地字段有什么不同:
static readonly decimal DecimalMinValue = Decimal.MinValue;
嗯,答案是 - 如果您的案件中的每一分钱都计算在内,则存在差异:
Method | Mean | StdDev |
--------------------------- |------------ |---------- |
CompareWithDecimalMinValue | 178.4235 ns | 0.4395 ns |
CompareWithLocalMinValue | 98.0991 ns | 2.2803 ns |
这种行为的原因是DecimalConstantAttribute,它指定了每次使用小数' s常数时如何创建小数。
问题是 - 我理解每次使用它时,调用Decimal构造函数的成本为'常数'影响几乎没有人,但......为什么它以这种方式实施?是因为C ++缺少复制构造函数的概念吗?
答案 0 :(得分:6)
如果decimal.MinValue
仅被声明为static readonly
字段,则无法在其他地方将其用作编译时常量 - 例如用于诸如可选参数的默认值之类的东西。
我认为BCL团队可以提供常量和只读字段,但这会让很多人感到困惑。如果你处于非常罕见的情况,它会产生影响,介绍你自己的领域看起来是一个完全合理的解决方法。
或者,编译器可以决定只在可行的情况下复制字段的值而不是使用构造函数。这可能会最终获得无法触及的内存 - 像这样的微调可能会导致意想不到的副作用。我怀疑让编译器处理这个简单的事情被认为比猜测在每种情况下效率最高的事情更重要。