Использование Reflection.Emit для создания блока using (x) {}?

Я пытаюсь использовать Reflection.Emit в С# для создания блока using (x) { ... }.

В тот момент, когда я нахожусь в коде, мне нужно взять текущую вершину стека, которая является объектом, который реализует IDisposable, сохранить его в локальной переменной, реализовать блок использования для этой переменной, а затем внутри него добавить еще несколько код (я могу справиться с этой последней частью.)

Вот пример кода на C#, который я пытался скомпилировать и просмотреть в Reflector:

public void Test()
{
    TestDisposable disposable = new TestDisposable();
    using (disposable)
    {
        throw new Exception("Test");
    }
}

В рефлекторе это выглядит так:

.method public hidebysig instance void Test() cil managed
{
    .maxstack 2
    .locals init (
        [0] class LVK.Reflection.Tests.UsingConstructTests/TestDisposable disposable,
        [1] class LVK.Reflection.Tests.UsingConstructTests/TestDisposable CS$3$0000,
        [2] bool CS$4$0001)
    L_0000: nop 
    L_0001: newobj instance void LVK.Reflection.Tests.UsingConstructTests/TestDisposable::.ctor()
    L_0006: stloc.0 
    L_0007: ldloc.0 
    L_0008: stloc.1 
    L_0009: nop 
    L_000a: ldstr "Test"
    L_000f: newobj instance void [mscorlib]System.Exception::.ctor(string)
    L_0014: throw 
    L_0015: ldloc.1 
    L_0016: ldnull 
    L_0017: ceq 
    L_0019: stloc.2 
    L_001a: ldloc.2 
    L_001b: brtrue.s L_0024
    L_001d: ldloc.1 
    L_001e: callvirt instance void [mscorlib]System.IDisposable::Dispose()
    L_0023: nop 
    L_0024: endfinally 
    .try L_0009 to L_0015 finally handler L_0015 to L_0025
}

Я понятия не имею, как справиться с этой частью ".try..." в конце при использовании Reflection.Emit.

Может ли кто-нибудь указать мне в правильном направлении?


Редактировать: после того, как меня спросили о коде по электронной почте, я опубликую здесь свой код интерфейса Fluent, но он никому не пригодится, если вы не возьмете некоторые из моих библиотек классов, а это также немного кода. Код, с которым я мучился, был частью моего проекта IoC, и мне нужно было сгенерировать класс для реализации автоматической регистрации вызовов методов в сервисе, по сути, класс-декоратор для сервисов, который автоматически генерирует код.

Основной цикл метода, реализующий все методы интерфейса, таков:

foreach (var method in interfaceType.GetMethods())
{
    ParameterInfo[] methodParameters = method.GetParameters();
    var parameters = string.Join(", ", methodParameters
        .Select((p, index) => p.Name + "={" + index + "}"));
    var signature = method.Name + "(" + parameters + ")";
    type.ImplementInterfaceMethod(method).GetILGenerator()
        // object[] temp = new object[param-count]
        .variable<object[]>() // #0
        .ldc(methodParameters.Length)
        .newarr(typeof(object))
        .stloc_0()
        // copy all parameter values into array
        .EmitFor(Enumerable.Range(0, methodParameters.Length), (il, i) => il
            .ldloc_0()
            .ldc(i)
            .ldarg_opt(i + 1)
            .EmitIf(methodParameters[i].ParameterType.IsValueType, a => a
                .box(methodParameters[i].ParameterType))
            .stelem(typeof(object))
        )
        // var x = _Logger.Scope(LogLevel.Debug, signature, parameterArray)
        .ld_this()
        .ldfld(loggerField)
        .ldc(LogLevel.Debug)
        .ldstr(signature)
        .ldloc(0)
        .call_smart(typeof(ILogger).GetMethod("Scope", new[] { typeof(LogLevel), typeof(string), typeof(object[]) }))
        // using (x) { ... }
        .EmitUsing(u => u
            .ld_this()
            .ldfld(instanceField)
            .ldargs(Enumerable.Range(1, methodParameters.Length).ToArray())
            .call_smart(method)
            .EmitCatch<Exception>((il, ex) => il
                .ld_this()
                .ldfld(loggerField)
                .ldc(LogLevel.Debug)
                .ldloc(ex)
                .call_smart(typeof(ILogger).GetMethod("LogException", new[] { typeof(LogLevel), typeof(Exception) }))
            )
        )
        .ret();
}

EmitUsing выдает BeginExceptionBlock, на который ответил Джон, так что это то, что мне нужно было знать.

Приведенный выше код взят из LoggingDecorator.cs, расширения IL в основном находятся в ILGeneratorExtensions.Designer.cs и другие файлы в папке LVK.Reflection.


person Lasse V. Karlsen    schedule 08.06.2010    source источник


Ответы (2)


Является ли ILGenerator.BeginExceptionBlock тем, что вы после? Пример в документах предполагает, что это правильный подход...

person Jon Skeet    schedule 08.06.2010
comment
Да, это было то, чего я добивался. Спасибо. Я опубликую код, если кто-то еще ищет что-то подобное. - person Lasse V. Karlsen; 08.06.2010
comment
Если подумать, я перестану публиковать свой код, у меня есть обширная библиотека методов расширения для Reflection.Emit, поэтому мне придется все это переписать, я сделаю это, если кто-то попросит, но мой текущий код будет вряд ли кому поможет, кроме меня. - person Lasse V. Karlsen; 08.06.2010

Вот пример, в коде.

ILGenerator ilg = ...;

// Begin the 'try' block. The returned label is at the end of the 'try' block.
// You can jump there and any finally blocks will be executed.
Label block = ilg.BeginExceptionBlock();

// ... emit operations that might throw

ilg.BeginFinallyBlock();

// ... emit operations within the finally block

ilg.EndExceptionBlock();
person Drew Noakes    schedule 02.04.2016