Using Reflection.Emit to emit a "using (x) { ... }" block?
Asked Answered
S

2

11

I'm trying to use Reflection.Emit in C# to emit a using (x) { ... } block.

At the point I am in code, I need to take the current top of the stack, which is an object that implements IDisposable, store this away in a local variable, implement a using block on that variable, and then inside it add some more code (I can deal with that last part.)

Here's a sample C# piece of code I tried to compile and look at in Reflector:

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

This looks like this in Reflector:

.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
}

I have no idea how to deal with that ".try ..." part at the end there when using Reflection.Emit.

Can someone point me in the right direction?


Edit: After asked about the code by email, I'll post my fluent interface code here, but it isn't going to be much use to anyone unless you grab some of my class libraries, and that's a bit of code as well. The code I was struggling with was part of my IoC project, and I needed to generate a class to implement automatic logging of method calls on a service, basically a decorator class for services that auto-generates the code.

The main loop of the method, that implements all the interface methods, is this:

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 spits out the BeginExceptionBlock that Jon answered with, so that's what I needed to know.

The above code is from LoggingDecorator.cs, the IL extensions are mostly in ILGeneratorExtensions.Designer.cs, and other files in the LVK.Reflection namespace.

Sponson answered 8/6, 2010 at 15:49 Comment(0)
U
11

Is ILGenerator.BeginExceptionBlock what you're after? The example in the docs suggests it's the right approach...

Unaccustomed answered 8/6, 2010 at 15:53 Comment(2)
Yes, this was what I was after. Thanks. I'll post the code if anyone else looks for something like this.Sponson
On second thought, I'll drop posting my code, I have an extensive library of extension methods for Reflection.Emit so I'll have to rewrite it all, I'll do it if someone asks for it, but my current code will probably not be helpful to anyone but me.Sponson
F
0

Here's an example, in code.

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();
Forspent answered 2/4, 2016 at 19:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.