私はすばらしいMSILの世界で取り掛かり始めましたが、境界チェックが発生する場所についてここでは答えが見つからないようです。 C#コンパイラは、境界チェックを実行するMSIL命令を挿入するのですか、それともMSIL JITコンパイラは、それらを機械語に変換する際に、適用可能なすべてのMSIL命令に挿入しますか?境界チェックロジックはMSILまたはマシンコードで発生します
MSILを直接使用して関数を生成するときに、これらの命令を追加する必要があるかどうかを知る必要があるため、私が尋ねるのはその理由です。
編集:1つの回答を受け取り、妥当性を確認しようとすると、間違っているように見えます。次のコードは実際には失敗します。デバッガのステップ実行すると、第二の試験は、配列の境界を超えて書き込みを行うことを明らかにし、3番目のテストははるかに悪化します:
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のみNULLポインタのチェック、NOT境界チェックを行うことが表示されます。
私は現在VS2012を使用しており、.NET Framework 4.5.1をターゲットにしています。
MSILにないため、ジッタは境界チェックを行うマシンコードを生成します。そして例外を生成するコード。インデックスが境界から外れることがないことがわかったときに、そのコードを*省略する方法を知っています。それがMSILにない究極の理由です。 –
@ HansPassant:そうではないようです。上記のコードで検証してください。 – James
あなたが何を証明しようとしているのか分かりにくいですが、C#もVB.NETコンパイラもOpCodes.Initblkを生成しません。それは検証可能な命令ではない。 C++/CLIコンパイラは、アンマネージC++コードに使用できます。 –