8 Ağustos 2019

C# Dinamik Kod Üretimi

System.Reflection.Emit namespace, metadata ve runtime sırasında IL oluşturmak için sınıflar içerir.
Bir DynamicMethod  ve ilişkili IL garbage-collector tarafından referans olarak verilmediği zaman toplanır. Yani belleği doldurmadan dinamik olarak methodlar oluşturup durabilirsiniz.

DynamicMethod Örneği:

static void Main()
        {
            var dynMeth = new DynamicMethod("Foo", null, null, typeof(Program));
            ILGenerator gen = dynMeth.GetILGenerator();
            gen.EmitWriteLine("Hello World");
            gen.Emit(OpCodes.Ret);
            dynMeth.Invoke(null, null);

        }

Method her zaman OpCodes.Ret ile biter. Return anlamına gelir.
EmitWriteLine  metodu ILGenerator da bir dizi düşük seviyedeki opcode ları yayınlayan kısayoldur.

Aynı sonucu Emit WriteLine ile alamak için aşağıda ki gibi kodu değiştirebiliriz.

 dynMeth = new DynamicMethod("Bar", null, null, typeof(Program));
gen = dynMeth.GetILGenerator();
MethodInfo mi = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) });
gen.Emit(OpCodes.Ldstr, "Hello World2");
gen.Emit(OpCodes.Call, mi);
gen.Emit(OpCodes.Ret);
dynMeth.Invoke(null,null);


Public olmayan methodlara ulaşmamız için örnek:

var dynMeth = new DynamicMethod("Tar", null, null, typeof(Program));
ILGenerator gen = dynMeth.GetILGenerator();
MethodInfo privateMethod = typeof(Program).GetMethod("HelloWorld", BindingFlags.NonPublic | BindingFlags.Static);   
gen.Emit(OpCodes.Call, privateMethod);
gen.Emit(OpCodes.Ret);
dynMeth.Invoke(null, null);

Parametre ile Toplama İşlemi

using System;
using System.Reflection.Emit;

namespace ConsoleApp2
{
    class Program
    {
        delegate int BinaryFunction(int n1, int n2);
        static void Main()
        {

            var dynMeth = new DynamicMethod("Sum",
                typeof(int), //Return Type = int
                new[] { typeof(int), typeof(int) }, // Parameter Types = int, int
                typeof(Program));
            ILGenerator gen = dynMeth.GetILGenerator();
            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Ldarg_1);
            gen.Emit(OpCodes.Add);
            gen.Emit(OpCodes.Ret);

            // Usage 1
            var result = (int)dynMeth.Invoke(null, new object[] { 4, 5 });
            Console.WriteLine(result);
            
            //Usage 2 // Comman Way
            BinaryFunction sumCommonWay = (BinaryFunction)dynMeth.CreateDelegate(typeof(BinaryFunction));
            result = sumCommonWay(3, 4);
            Console.WriteLine(result);

           
            Console.ReadKey();
        }

    }
}



// Yerel Değişken Ekleme ve Döngüler 
using System;
using System.Reflection.Emit;

namespace ConsoleApp2
{
    class Program
    {
        static void Main()
        {
            var dynMeth = new DynamicMethod("Sum4", typeof(int), new[] { typeof(int) }, typeof(Program));
            ILGenerator gen = dynMeth.GetILGenerator();
            Label startLoop = gen.DefineLabel(); // Declare labels
            Label endLoop = gen.DefineLabel();

            LocalBuilder i = gen.DeclareLocal(typeof(int)); // int i for index
            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Stloc, i);

            LocalBuilder sum = gen.DeclareLocal(typeof(int)); // int sum for from 0 to i initial value
            gen.Emit(OpCodes.Ldc_I4,0);
            gen.Emit(OpCodes.Stloc, sum); // set value for local sum

            gen.MarkLabel(startLoop);

            gen.Emit(OpCodes.Ldc_I4, 1); // Load 10 onto eval stack
            gen.Emit(OpCodes.Ldloc, i); // Load x onto eval stack
            gen.Emit(OpCodes.Bgt, endLoop); // if (x > 10) goto endLoop


            gen.Emit(OpCodes.Ldloc, i); // Load x onto eval stack
            gen.Emit(OpCodes.Ldloc, sum); // Load x onto eval stack
            gen.Emit(OpCodes.Add);
            gen.Emit(OpCodes.Stloc, sum); // Save result back to x

            gen.Emit(OpCodes.Ldloc, i); // Load x onto eval stack
            gen.Emit(OpCodes.Ldc_I4,  -1); // Load -1 onto the stack
            gen.Emit(OpCodes.Add); // Add them together
            gen.Emit(OpCodes.Stloc, i); // Save result back to x
           

            gen.Emit(OpCodes.Br, startLoop); // return to start of loop
            gen.MarkLabel(endLoop);
            gen.Emit(OpCodes.Ldloc, sum); //
            gen.Emit(OpCodes.Ret);

            var res = (int)dynMeth.Invoke(null, new object[] { 15 });

            Console.WriteLine(res);
            Console.ReadKey();
        }

    }
}