En utilisant Reflection.Emit pour émettre un « en utilisant (x) {...} » bloc?
-
24-10-2019 - |
Question
Je suis en train d'utiliser Reflection.Emit en C # pour émettre un bloc using (x) { ... }
.
Au point où je suis dans le code, je dois prendre le haut courant de la pile, qui est un objet qui implémente IDisposable, stocker cette distance dans une variable locale, mettre en œuvre un bloc à l'aide de cette variable, puis à l'intérieur ajouter un peu plus de code (je peux faire face à cette dernière partie.)
Voici un morceau échantillon C # de code que j'essayé de compiler et de regarder dans le réflecteur:
public void Test()
{
TestDisposable disposable = new TestDisposable();
using (disposable)
{
throw new Exception("Test");
}
}
Cela ressemble à ce réflecteur:
.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
}
Je ne sais pas comment faire face à cette partie « .Essayez ... » à la fin il lors de l'utilisation Reflection.Emit.
point que quelqu'un peut me dans la bonne direction?
Modifier : Après demandé sur le code par e-mail, je posterai mon code d'interface fluide, mais il ne va pas être beaucoup plus utile à personne, sauf si vous prenez certains de mes bibliothèques de classes , et c'est un peu de code. Le code que je luttais avec faisait partie de mon projet IoC, et je devais générer une classe pour mettre en œuvre l'enregistrement automatique des appels de méthode sur un service, essentiellement une classe de décorateur pour les services qui génère automatiquement le code.
La boucle principale du procédé, qui met en oeuvre toutes les méthodes de l'interface, est la suivante:
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 recrache la BeginExceptionBlock que Jon a répondu avec, de sorte que ce que je voulais savoir.
Le code ci-dessus est de LoggingDecorator.cs , les extensions IL sont la plupart du temps dans ILGeneratorExtensions.Designer.cs , et d'autres fichiers dans le LVK.Reflection espace de noms.
La solution
ILGenerator.BeginExceptionBlock
ce vous êtes après? L'exemple dans la documentation donne à penser qu'il est la bonne approche ...
Autres conseils
Voici un exemple, dans le 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();