Pregunta

Estoy tratando de usar la reflexión. EMIT en C# para emitir un using (x) { ... } bloquear.

En el punto que estoy en el código, necesito tomar la parte superior actual de la pila, que es un objeto que implementa idisible, almacene esto en una variable local, implementa un bloque de uso en esa variable y luego agregue un poco más Código (puedo lidiar con esa última parte).

Aquí hay una muestra de código C# que intenté compilar y ver en Reflector:

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

Esto se ve así en 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
}

No tengo idea de cómo lidiar con esa parte ".try ..." al final cuando se usa reflexion.emit.

¿Alguien me puede apuntar en la dirección correcta?


Editar: Después de preguntar sobre el código por correo electrónico, publicaré mi código de interfaz fluido aquí, pero no será muy útil para nadie a menos que tome algunas de las bibliotecas de mis clases, y eso también es un poco de código. El código con el que estaba luchando era parte de mi proyecto IOC, y necesitaba generar una clase para implementar el registro automático de llamadas de método en un servicio, básicamente una clase de decorador para servicios que generan automáticamente el código.

El bucle principal del método, que implementa todos los métodos de interfaz, es este:

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 escupe el bloque de Beginexception con el que Jon respondió, así que eso es lo que necesitaba saber.

El código anterior es de LoggingDecorator.cs, las extensiones de IL están principalmente en IlGeneratorextensions.designer.cs, y otros archivos en el Lvk. Reflexión espacio de nombres.

¿Fue útil?

Solución

Es ILGenerator.BeginExceptionBlock ¿Qué buscas? El ejemplo en los documentos sugiere que es el enfoque correcto ...

Otros consejos

Aquí hay un ejemplo, en código.

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();
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top