ILGenerator выбрасывает отпуск после броска

#c# #cil #ilgenerator

#c# #cil #ilgenerator

Вопрос:

Я генерирую метод с помощью ilgenerator, и у меня проблема. IlGenerator.BeginCatchBlock() принудительно завершает работу, и в этом случае это недоступная инструкция.

 using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
namespace Stack
{
    class Program
    {
        static void Main(string[] args)
        {
           var asmName = new AssemblyName("Test");
        var domain = Thread.GetDomain();
        var assembly = domain.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndSave);
        var module = assembly.DefineDynamicModule("MyModule", "Test.dll", true);
        var type = module.DefineType("MyType");
        var method = type.DefineMethod("MyMethod", MethodAttributes.Public);
        var ilg = method.GetILGenerator();
        ilg.BeginExceptionBlock();
        ilg.ThrowException(typeof(Exception));
        ilg.BeginCatchBlock(typeof(Exception));
        ilg.Emit(OpCodes.Pop);
        ilg.EndExceptionBlock();
        ilg.Emit(OpCodes.Ret);
        type.CreateType();
        assembly.Save("test.dll");
        }
    }
}
  

На самом деле, вывод выглядит как

  .method public instance void MyMethod () cil managed 
    {
            .maxstack 1
            .try
        {
            IL_0000: newobj instance void [mscorlib]System.Exception::.ctor()
            IL_0005: throw
            IL_0006: leave IL_0011
        } // end .try
        catch [mscorlib]System.Exception
        {
            IL_000b: pop
            IL_000c: leave IL_0011
        } // end handler
        IL_0011: ret
   }
  

Я ожидал, что у меня не будет leave IL_0011 в блоке .try. PEVerify не показывает ошибок.

Комментарии:

1. Просто из любопытства, почему это проблема? Я понимаю, что IL_0006: leave IL_0011 это недоступно, но это кажется достаточно безобидным?

2. Взгляните на ссылочный источник. ILGenerator Функции, которые связаны с обработкой ошибок, сами по себе выдают несколько кодов операций, и do ничего не может с этим поделать.

3. Надежное обнаружение недоступного кода сводится к проблеме остановки. В этом случае любой приличный компилятор мог бы увидеть, что инструкция недоступна, но Reflection.Emit не является компилятором, и поэтому он просто всегда выдает leave в конце блока, не прилагая больше усилий и не предоставляя вам возможность опустить ее, потому что вы знаете лучше. По той же причине PEVerify не жалуется, потому что недоступный IL-код не является ошибкой, он просто избыточен. Вы обнаружите, что такого рода «неоптимальный» IL на самом деле распространен, потому что это проще, и предполагается, что JIT-компилятор восполнит недостаток.

4. Вы рассказали историю: у вас проблема, вы написали какой-то код, он сделал то, чего вы не ожидали. Это совершенно хорошая история, но здесь нет вопроса, на который мы могли бы ответить. В чем ваш вопрос? Если ваш подразумеваемый вопрос «что мне с этим делать?», то мой совет таков: когда ваши ожидания противоречат, научитесь признавать, что ваши ожидания были неправильными, и корректируйте их, чтобы они соответствовали реальности. Если это не ваш вопрос, то на самом деле задайте вопрос .