2016-04-14 28 views
1

我刚开始涉足MSIL的美妙世界,但我似乎无法找到任何有关边界检查发生的答案。 C#编译器是否插入执行边界检查的MSIL指令,或者MSIL JIT编译器是否将所有适用的MSIL指令转换为机器代码时插入它们?边界检查逻辑是否发生在MSIL或机器代码

我问的原因是因为我需要知道当我直接使用MSIL生成函数时是否需要添加这些指令。

编辑:收到一个答案并尝试验证后,它似乎是不正确的。下面的代码实际上失败了。通过调试器步进显示,第二测试将超出数组边界,第三个测试确实糟糕得多:

static class ArrayExtensions 
{ 
#if false 
    public static void ClearRangeReferenceImplementation(byte[] buffer, int offset, int byteCount) 
    { 
     for (int current = offset; current < offset + byteCount; ++current) 
     { 
      buffer[current] = 0; 
     } 
    } 
#endif 
    private static readonly Action<IntPtr, int, int> MemclearRaw = InitMemclearRaw(); 
    private static Action<IntPtr, int, int> InitMemclearRaw() 
    { 
     DynamicMethod memclearMethod = new DynamicMethod("Memclear", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, null, new Type[] { typeof(IntPtr), typeof(int), typeof(int) }, typeof(this), true); 
     ILGenerator il = memclearMethod.GetILGenerator(); 
     il.Emit(OpCodes.Ldarg_0); 
     il.Emit(OpCodes.Ldarg_1); 
     il.Emit(OpCodes.Add); 
     il.Emit(OpCodes.Ldc_I4_0); 
     il.Emit(OpCodes.Ldarg_2); 
     il.Emit(OpCodes.Initblk); 
     il.Emit(OpCodes.Ret); 
     return (Action<IntPtr, int, int>)memclearMethod.CreateDelegate(typeof(Action<IntPtr, int, int>)); 
    } 
    /// <summary> 
    /// Clears the specified range of the specified buffer in the most optimal manner available without resorting to PInvoke or inline assembly. 
    /// </summary> 
    /// <param name="buffer">The buffer to acted upon.</param> 
    /// <param name="offset">The offset in the buffer where the clearing is to start.</param> 
    /// <param name="count">The number of bytes to be cleared.</param> 
    public static void ClearRange(this byte[] buffer, int offset, int count) 
    { 
     if (count == 0) return; 
     GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); 
     try 
     { 
      MemclearRaw(handle.AddrOfPinnedObject(), offset, count); 
     } 
     finally 
     { 
      handle.Free(); 
     } 
    } 
} 
[TestClass] 
public class TestArrayExtensions 
{ 
    /// <summary> 
    /// Performs failure tests on <see cref="ArrayExtensions.ClearRange"/>. 
    /// </summary> 
    [TestMethod] 
    public void Array_ClearRangeExceptions() 
    { 
     byte[] b = Rand.NewBytes(0, 100); 
     AssertExceptionClearRange(null, 0, b.Length, typeof(NullReferenceException)); 
     b = Rand.NewBytes(0, 100); 
     AssertExceptionClearRange(b, 0, b.Length + 1, typeof(ArgumentOutOfRangeException)); 
     b = Rand.NewBytes(0, 100); 
     AssertExceptionClearRange(b, 0, -1, typeof(ArgumentOutOfRangeException)); 
     b = Rand.NewBytes(0, 100); 
     AssertExceptionClearRange(b, -1, b.Length, typeof(ArgumentOutOfRangeException)); 
    } 

    private static void AssertExceptionClearRange(byte[] buffer, int offset, int count, Type exceptionType) 
    { 
     try 
     { 
      ArrayExtensions.ClearRange(buffer, offset, count); 
      Assert.Fail("ArrayExtensions.ClearRange did not throw the expected exception!"); 
     } 
     catch (Exception ex) 
     { 
      Assert.AreEqual(exceptionType, ex.GetType()); 
     } 
    } 

看来,MSIL只做空指针检查,而不是边界检查。

我目前正在使用VS2012并以.NET Framework 4.5.1为目标,如果这有什么区别的话。

+2

它不在MSIL中,抖动会生成机器码来执行边界检查。和生成异常的代码。当知道索引永远不会超出范围时,它知道如何忽略那些代码,这是它不在MSIL中的最终原因。 –

+0

@HansPassant:看起来并非如此。请使用上面的代码进行验证。 – James

+0

很难看到你想要证明什么,C#和VB.NET编译器都不会生成OpCodes.Initblk。这不是一个可验证的指令。 C++/CLI编译器可以将它用于非托管C++代码。 –

回答

2

C#编译器的优化很少。

所有的魔法都发生在生成机器码的JIT中。

要回答这个问题,如果您生成类似于C#或VB.NET的代码(如果JIT可以确定这是某种形式的for循环等),它就会起作用。