Thomas Darimont
Erfahrenes Mitglied
Hallo,
hier mal ein kleines Beispiel wie man mit System.Reflection.Emit dynamisch zur Laufzeit neuen IL-Code erzeugen lassen / und ausführen kann.
In meinem Beispiel habe ich ein einfach Programm zum finden von Primzahlen.
Das Interface IPrimeFinder deklariert die Methode bool IsPrime(int number);
Die Klasse PrimeFinder implementiert das IPrimeFinder und verwendet einen einfachen Algorithmus zur Entscheidung ob eine Zahl prim ist oder nicht:
Diesen Code möchte ich gerne 1:1 in IL zur Laufzeit generieren. Dazu erzeuge ich zur Laufzeit dynamisch einen neuen Type innerhalb einer dynamisch erzeugten Assembly welcher dann das Interface IPrimeFinder implementiert.
Wenn man sich den obigen Algorithmus mal in IL Code anschaut (beispielsweise mit dem .Net Reflector http://www.tutorials.de/forum/net-cafe/229672-net-decompiler-reflector.html ) so sieht man folgendes:
Diesen IL Code muss man nur noch mit den entsprechenden Mitteln (System.Reflection.Emit) etwas umformen und schon kanns losgehen 
Das Resultat sieht man dann hier:
Ausgabe:
Btw. wem das gefummel mit den Low-Level OpCodes zu mühsam ist der kann auch alternativ zum komfortablen http://wiki.castleproject.org/index.php/DynamicProxy greifen und die dortigen IL-Generierungsmechanismen verwenden (EasyType / EasyMethod etc.) damit wird's wirklich "Easy"
Mehr zum Thema (C)IL, der Architektur, einzelnen OpCodes findet man hier:
http://msdn2.microsoft.com/en-us/netframework/aa497266.aspx
Gruß Tom
hier mal ein kleines Beispiel wie man mit System.Reflection.Emit dynamisch zur Laufzeit neuen IL-Code erzeugen lassen / und ausführen kann.
In meinem Beispiel habe ich ein einfach Programm zum finden von Primzahlen.
Das Interface IPrimeFinder deklariert die Methode bool IsPrime(int number);
Die Klasse PrimeFinder implementiert das IPrimeFinder und verwendet einen einfachen Algorithmus zur Entscheidung ob eine Zahl prim ist oder nicht:
C#:
public bool IsPrime(int number)
{
if (number < 2)
{
return false;
}
if (2 == number)
{
return true;
}
if (0 == number % 2)
{
return false;
}
int sqrt = (int)Math.Sqrt(number);
for (int i = 3; i <= sqrt; i += 2)
{
if (0 == number % i)
{
return false;
}
}
return true;
}
Diesen Code möchte ich gerne 1:1 in IL zur Laufzeit generieren. Dazu erzeuge ich zur Laufzeit dynamisch einen neuen Type innerhalb einer dynamisch erzeugten Assembly welcher dann das Interface IPrimeFinder implementiert.
Wenn man sich den obigen Algorithmus mal in IL Code anschaut (beispielsweise mit dem .Net Reflector http://www.tutorials.de/forum/net-cafe/229672-net-decompiler-reflector.html ) so sieht man folgendes:
Code:
.method public hidebysig newslot virtual final instance bool IsPrime(int32 number) cil managed
{
.maxstack 3
.locals init (
[0] int32 sqrt,
[1] int32 i,
[2] bool CS$1$0000,
[3] bool CS$4$0001)
L_0000: nop
L_0001: ldarg.1
L_0002: ldc.i4.2
L_0003: clt
L_0005: ldc.i4.0
L_0006: ceq
L_0008: stloc.3
L_0009: ldloc.3
L_000a: brtrue.s L_0011
L_000c: nop
L_000d: ldc.i4.0
L_000e: stloc.2
L_000f: br.s L_0067
L_0011: ldc.i4.2
L_0012: ldarg.1
L_0013: ceq
L_0015: ldc.i4.0
L_0016: ceq
L_0018: stloc.3
L_0019: ldloc.3
L_001a: brtrue.s L_0021
L_001c: nop
L_001d: ldc.i4.1
L_001e: stloc.2
L_001f: br.s L_0067
L_0021: ldc.i4.0
L_0022: ldarg.1
L_0023: ldc.i4.2
L_0024: rem
L_0025: ceq
L_0027: ldc.i4.0
L_0028: ceq
L_002a: stloc.3
L_002b: ldloc.3
L_002c: brtrue.s L_0033
L_002e: nop
L_002f: ldc.i4.0
L_0030: stloc.2
L_0031: br.s L_0067
L_0033: ldarg.1
L_0034: conv.r8
L_0035: call float64 [mscorlib]System.Math::Sqrt(float64)
L_003a: conv.i4
L_003b: stloc.0
L_003c: ldc.i4.3
L_003d: stloc.1
L_003e: br.s L_0058
L_0040: nop
L_0041: ldc.i4.0
L_0042: ldarg.1
L_0043: ldloc.1
L_0044: rem
L_0045: ceq
L_0047: ldc.i4.0
L_0048: ceq
L_004a: stloc.3
L_004b: ldloc.3
L_004c: brtrue.s L_0053
L_004e: nop
L_004f: ldc.i4.0
L_0050: stloc.2
L_0051: br.s L_0067
L_0053: nop
L_0054: ldloc.1
L_0055: ldc.i4.2
L_0056: add
L_0057: stloc.1
L_0058: ldloc.1
L_0059: ldloc.0
L_005a: cgt
L_005c: ldc.i4.0
L_005d: ceq
L_005f: stloc.3
L_0060: ldloc.3
L_0061: brtrue.s L_0040
L_0063: ldc.i4.1
L_0064: stloc.2
L_0065: br.s L_0067
L_0067: ldloc.2
L_0068: ret
}

Das Resultat sieht man dann hier:
C#:
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
namespace De.Tutorials.Training
{
public class ILCodeGenerationExample
{
public static void Main(string[] args)
{
IPrimeFinder primeFinder = new PrimeFinder();
IPrimeFinder generatedPrimeFinder = (IPrimeFinder)GeneratePrimeFinder();
for (int i = 0; i < 1000;i++ )
{
if (primeFinder.IsPrime(i))
{
Console.Write(i+" ");
}
}
Console.WriteLine();
for (int i = 0; i < 1000; i++)
{
if (generatedPrimeFinder.IsPrime(i))
{
Console.Write(i + " ");
}
}
Console.WriteLine();
Console.WriteLine(int.MaxValue + " " + primeFinder.IsPrime(int.MaxValue));
Console.WriteLine(int.MaxValue + " " + generatedPrimeFinder.IsPrime(int.MaxValue));
}
private static IPrimeFinder GeneratePrimeFinder()
{
AssemblyName generatedAssemblyName = new AssemblyName("DynamicAssembly#" + DateTime.Now.Ticks);
AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(generatedAssemblyName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(generatedAssemblyName.Name, generatedAssemblyName.Name + ".dll", true);
TypeBuilder typeBuilder = moduleBuilder.DefineType("GeneratedPrimeFinder", TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit, typeof(object), new Type[] { typeof(IPrimeFinder) });
MethodBuilder methodBuilder = typeBuilder.DefineMethod("IsPrime", MethodAttributes.Public | MethodAttributes.NewSlot | MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Final , typeof(bool), new Type[] { typeof(int) });
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
ILGenerator methodIlGenerator = methodBuilder.GetILGenerator();
// .locals init (
//[0] int32 sqrt,
//[1] int32 i,
//[2] bool CS$1$0000,
//[3] bool CS$4$0001)
LocalBuilder sqrtLocal = methodIlGenerator.DeclareLocal(typeof(int));
sqrtLocal.SetLocalSymInfo("sqrt");
LocalBuilder iLocal = methodIlGenerator.DeclareLocal(typeof(int));
iLocal.SetLocalSymInfo("i");
LocalBuilder cSS1S0000Local = methodIlGenerator.DeclareLocal(typeof(bool));
cSS1S0000Local.SetLocalSymInfo("CSS1S0000");
LocalBuilder cSS4S0001Local = methodIlGenerator.DeclareLocal(typeof(bool));
cSS4S0001Local.SetLocalSymInfo("CSS4S0001");
Label label0011 = methodIlGenerator.DefineLabel();
Label label0021 = methodIlGenerator.DefineLabel();
Label label0033 = methodIlGenerator.DefineLabel();
Label label0040 = methodIlGenerator.DefineLabel();
Label label0053 = methodIlGenerator.DefineLabel();
Label label0058 = methodIlGenerator.DefineLabel();
Label label0067Return = methodIlGenerator.DefineLabel();
methodIlGenerator.Emit(OpCodes.Nop);
methodIlGenerator.Emit(OpCodes.Ldarg_1);
methodIlGenerator.Emit(OpCodes.Ldc_I4_2);
methodIlGenerator.Emit(OpCodes.Clt);
methodIlGenerator.Emit(OpCodes.Ldc_I4_0);
methodIlGenerator.Emit(OpCodes.Ceq);
methodIlGenerator.Emit(OpCodes.Stloc_3);
methodIlGenerator.Emit(OpCodes.Ldloc_3);
methodIlGenerator.Emit(OpCodes.Brtrue_S, label0011);
methodIlGenerator.Emit(OpCodes.Nop);
methodIlGenerator.Emit(OpCodes.Ldc_I4_0);
methodIlGenerator.Emit(OpCodes.Stloc_2);
methodIlGenerator.Emit(OpCodes.Br_S, label0067Return);
//Label label0011 = methodIlGenerator.DefineLabel();
methodIlGenerator.MarkLabel(label0011);
methodIlGenerator.Emit(OpCodes.Ldc_I4_2);
methodIlGenerator.Emit(OpCodes.Ldarg_1);
methodIlGenerator.Emit(OpCodes.Ceq);
methodIlGenerator.Emit(OpCodes.Ldc_I4_0);
methodIlGenerator.Emit(OpCodes.Ceq);
methodIlGenerator.Emit(OpCodes.Stloc_3);
methodIlGenerator.Emit(OpCodes.Ldloc_3);
methodIlGenerator.Emit(OpCodes.Brtrue_S, label0021);
methodIlGenerator.Emit(OpCodes.Nop);
methodIlGenerator.Emit(OpCodes.Ldc_I4_1);
methodIlGenerator.Emit(OpCodes.Stloc_2);
methodIlGenerator.Emit(OpCodes.Br_S, label0067Return);
//Label label0021 = methodIlGenerator.DefineLabel();
methodIlGenerator.MarkLabel(label0021);
methodIlGenerator.Emit(OpCodes.Ldc_I4_0);
methodIlGenerator.Emit(OpCodes.Ldarg_1);
methodIlGenerator.Emit(OpCodes.Ldc_I4_2);
methodIlGenerator.Emit(OpCodes.Rem);
methodIlGenerator.Emit(OpCodes.Ceq);
methodIlGenerator.Emit(OpCodes.Ldc_I4_0);
methodIlGenerator.Emit(OpCodes.Ceq);
methodIlGenerator.Emit(OpCodes.Stloc_3);
methodIlGenerator.Emit(OpCodes.Ldloc_3);
methodIlGenerator.Emit(OpCodes.Brtrue_S, label0033);
methodIlGenerator.Emit(OpCodes.Nop);
methodIlGenerator.Emit(OpCodes.Ldc_I4_0);
methodIlGenerator.Emit(OpCodes.Stloc_2);
methodIlGenerator.Emit(OpCodes.Br_S, label0067Return);
//Label label0033 = methodIlGenerator.DefineLabel();
methodIlGenerator.MarkLabel(label0033);
methodIlGenerator.Emit(OpCodes.Ldarg_1);
methodIlGenerator.Emit(OpCodes.Conv_R8);
methodIlGenerator.Emit(OpCodes.Call, typeof(Math).GetMethod("Sqrt"));
//methodIlGenerator.Emit(OpCodes.Call, float64 [mscorlib]System.Math::Sqrt(OpCodes.
methodIlGenerator.Emit(OpCodes.Conv_I4);
methodIlGenerator.Emit(OpCodes.Stloc_0);
methodIlGenerator.Emit(OpCodes.Ldc_I4_3);
methodIlGenerator.Emit(OpCodes.Stloc_1);
methodIlGenerator.Emit(OpCodes.Br_S, label0058);
//Label label0040 = methodIlGenerator.DefineLabel();
methodIlGenerator.MarkLabel(label0040);
methodIlGenerator.Emit(OpCodes.Nop);
methodIlGenerator.Emit(OpCodes.Ldarg_1);
methodIlGenerator.Emit(OpCodes.Ldloc_1);
methodIlGenerator.Emit(OpCodes.Rem);
methodIlGenerator.Emit(OpCodes.Ldc_I4_0);
methodIlGenerator.Emit(OpCodes.Ceq);
methodIlGenerator.Emit(OpCodes.Ldc_I4_0);
methodIlGenerator.Emit(OpCodes.Ceq);
methodIlGenerator.Emit(OpCodes.Stloc_3);
methodIlGenerator.Emit(OpCodes.Ldloc_3);
methodIlGenerator.Emit(OpCodes.Brtrue_S, label0053);
methodIlGenerator.Emit(OpCodes.Nop);
methodIlGenerator.Emit(OpCodes.Ldc_I4_0);
methodIlGenerator.Emit(OpCodes.Stloc_2);
methodIlGenerator.Emit(OpCodes.Br_S, label0067Return);
//Label label0053 = methodIlGenerator.DefineLabel();
methodIlGenerator.MarkLabel(label0053);
methodIlGenerator.Emit(OpCodes.Nop);
methodIlGenerator.Emit(OpCodes.Ldloc_1);
methodIlGenerator.Emit(OpCodes.Ldc_I4_2);
methodIlGenerator.Emit(OpCodes.Add);
methodIlGenerator.Emit(OpCodes.Stloc_1);
//Label label0058 = methodIlGenerator.DefineLabel();
methodIlGenerator.MarkLabel(label0058);
methodIlGenerator.Emit(OpCodes.Ldloc_1);
methodIlGenerator.Emit(OpCodes.Ldloc_0);
methodIlGenerator.Emit(OpCodes.Cgt);
methodIlGenerator.Emit(OpCodes.Ldc_I4_0);
methodIlGenerator.Emit(OpCodes.Ceq);
methodIlGenerator.Emit(OpCodes.Stloc_3);
methodIlGenerator.Emit(OpCodes.Ldloc_3);
methodIlGenerator.Emit(OpCodes.Brtrue_S, label0040);
methodIlGenerator.Emit(OpCodes.Ldc_I4_1);
methodIlGenerator.Emit(OpCodes.Stloc_2);
methodIlGenerator.Emit(OpCodes.Br_S, label0067Return);
//Label label0067 = methodIlGenerator.DefineLabel();
methodIlGenerator.MarkLabel(label0067Return);
methodIlGenerator.Emit(OpCodes.Ldloc_2);
methodIlGenerator.Emit(OpCodes.Ret);
Type generatedPrimeFinderType = typeBuilder.CreateType();
return (IPrimeFinder)Activator.CreateInstance(generatedPrimeFinderType);
}
}
public interface IPrimeFinder
{
bool IsPrime(int number);
}
public class PrimeFinder : IPrimeFinder
{
public bool IsPrime(int number)
{
if (number < 2)
{
return false;
}
if (2 == number)
{
return true;
}
if (0 == number % 2)
{
return false;
}
int sqrt = (int)Math.Sqrt(number);
for (int i = 3; i <= sqrt; i += 2)
{
if (0 == number % i)
{
return false;
}
}
return true;
}
}
}
Ausgabe:
Code:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997
2147483647 True
2147483647 True
Drücken Sie eine beliebige Taste . . .
Btw. wem das gefummel mit den Low-Level OpCodes zu mühsam ist der kann auch alternativ zum komfortablen http://wiki.castleproject.org/index.php/DynamicProxy greifen und die dortigen IL-Generierungsmechanismen verwenden (EasyType / EasyMethod etc.) damit wird's wirklich "Easy"

Mehr zum Thema (C)IL, der Architektur, einzelnen OpCodes findet man hier:
http://msdn2.microsoft.com/en-us/netframework/aa497266.aspx
Gruß Tom