假设我们有:
public void TestIndex1(int index)
{
if(index < 0 || index >= _size)
ThrowHelper.ThrowArgumentOutOfRangeException();
}
public void TestIndex2(int index)
{
if((uint)index >= (uint)_size)
ThrowHelper.ThrowArgumentOutOfRangeException();
}
让我们编译这些代码并查看ILSpy:
.method public hidebysig
instance void TestIndex1 (
int32 index
) cil managed
{
IL_0000: ldarg.1
IL_0001: ldc.i4.0
IL_0002: blt.s IL_000d
IL_0004: ldarg.1
IL_0005: ldarg.0
IL_0006: ldfld int32 TempTest.TestClass::_size
IL_000b: bge.s IL_0012
IL_000d: call void TempTest.ThrowHelper::ThrowArgumentOutOfRangeException()
IL_0012: ret
}
.method public hidebysig
instance void TestIndex2 (
int32 index
) cil managed
{
IL_0000: ldarg.1
IL_0001: ldarg.0
IL_0002: ldfld int32 TempTest.TestClass::_size
IL_0007: blt.un.s IL_000e
IL_0009: call void TempTest.ThrowHelper::ThrowArgumentOutOfRangeException()
IL_000e: ret
}
不难看出,第二个代码更少,分支更少。
真的,根本没有强制转换,可以选择使用blt.s
和bge.s
还是使用blt.s.un
,其中后者将传递的整数视为无符号,而前者将它们视为有符号。
(注意对于那些不熟悉CIL,因为这是与CIL答案一个C#问题,bge.s
,blt.s
和blt.s.un
是“短”的版本bge
,blt
和blt.un
分别blt
离开本层两个值和分支如果第一小于所述第二时blt.un
当将它们视为无符号值时,将它们视为有符号值,同时弹出堆栈的两个值并分支(如果第一个小于第二个值,则分支)。
完全是微型选择,但有时候值得做。进一步考虑,与方法主体中的其余代码一起,这可能意味着某些内容是否落在抖动限制(是否为内联)之内,并且如果他们不愿意使用帮助程序抛出超出范围的异常,则它们是可能会尝试尽可能确保内联,并且额外的4个字节可能会有所不同。
确实,内联差异很有可能比减少一个分支机构要大得多。没有很多时间竭尽全力确保进行内联值得,但是一类如此大量使用的核心方法List<T>
肯定会是其中之一。