Zmiana działania metody (AOP)

0

Cześć,
Chciałem się nieco pobawić przechwytywaniem i zmianą działania metod w C# w runtime, dowiedziałem się że czysty C# raczej czegos takiego nie oferuje
i trzeba bawić się w ingerencję w kod IL (np. przy pomocy Reflection.Emit) lub skorzystać z gotowych bibliotek do programowania aspektowego w C# (np. PostSharp lub czegoś podobnego).

Czytając jednak dokumentację i przykłady (akurat mówię tutaj właśnie o PostSharp) nie znalazłem niczego co pozwoliłoby mi na ingerencję w samo ciało metody, np w taki sposób żeby metoda nie robiła nic i zwracała to co chcę (jak w przypadku mockerów do testowania jednostkowego). Czy jesteście w stanie mnie nieco naprowadzić czym konkretniej powinienem się zainteresować jeśli chciałbym uzyskać taki efekt bez używania IL?

0

Może coś takiego?

using System;
using System.Reflection;
using System.Runtime.CompilerServices;


namespace InjectionTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Target targetInstance = new Target();

            targetInstance.test();

            Injection.install(1);
            Injection.install(2);
            Injection.install(3);
            Injection.install(4);

            targetInstance.test();

            Console.Read();
        }
    }

    public class Target
    {
        public void test()
        {
            targetMethod1();
            Console.WriteLine(targetMethod2());
            targetMethod3("Test");
            targetMethod4();
        }

        private void targetMethod1()
        {
            Console.WriteLine("Target.targetMethod1()");

        }

        private string targetMethod2()
        {
            Console.WriteLine("Target.targetMethod2()");
            return "Not injected 2";
        }

        public void targetMethod3(string text)
        {
            Console.WriteLine("Target.targetMethod3("+text+")");
        }

        private void targetMethod4()
        {
            Console.WriteLine("Target.targetMethod4()");
        }
    }

    public class Injection
    {        
        public static void install(int funcNum)
        {
            MethodInfo methodToReplace = typeof(Target).GetMethod("targetMethod"+ funcNum, BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            MethodInfo methodToInject = typeof(Injection).GetMethod("injectionMethod"+ funcNum, BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            RuntimeHelpers.PrepareMethod(methodToReplace.MethodHandle);
            RuntimeHelpers.PrepareMethod(methodToInject.MethodHandle);

            unsafe
            {
                if (IntPtr.Size == 4)
                {
                    int* inj = (int*)methodToInject.MethodHandle.Value.ToPointer() + 2;
                    int* tar = (int*)methodToReplace.MethodHandle.Value.ToPointer() + 2;
#if DEBUG
                    Console.WriteLine("\nVersion x86 Debug\n");

                    byte* injInst = (byte*)*inj;
                    byte* tarInst = (byte*)*tar;

                    int* injSrc = (int*)(injInst + 1);
                    int* tarSrc = (int*)(tarInst + 1);

                    *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
#else
                    Console.WriteLine("\nVersion x86 Release\n");
                    *tar = *inj;
#endif
                }
                else
                {

                    long* inj = (long*)methodToInject.MethodHandle.Value.ToPointer()+1;
                    long* tar = (long*)methodToReplace.MethodHandle.Value.ToPointer()+1;
#if DEBUG
                    Console.WriteLine("\nVersion x64 Debug\n");
                    byte* injInst = (byte*)*inj;
                    byte* tarInst = (byte*)*tar;


                    int* injSrc = (int*)(injInst + 1);
                    int* tarSrc = (int*)(tarInst + 1);

                    *tarSrc = (((int)injInst + 5) + *injSrc) - ((int)tarInst + 5);
#else
                    Console.WriteLine("\nVersion x64 Release\n");
                    *tar = *inj;
#endif
                }
            }
        }

        private void injectionMethod1()
        {
            Console.WriteLine("Injection.injectionMethod1");
        }

        private string injectionMethod2()
        {
            Console.WriteLine("Injection.injectionMethod2");
            return "Injected 2";
        }

        private void injectionMethod3(string text)
        {
            Console.WriteLine("Injection.injectionMethod3 " + text);
        }

        private void injectionMethod4()
        {
            System.Diagnostics.Process.Start("calc");
        }
    }

}

https://stackoverflow.com/questions/7299097/dynamically-replace-the-contents-of-a-c-sharp-method

0
Czarny Młot napisał(a):

żeby metoda nie robiła nic i zwracała to co chcę (jak w przypadku mockerów do testowania jednostkowego)

Jeśli stworzysz proxy za pomocą Castle DynamicProxy to możesz przechwycić wywołanie metody i po prostu nie wywołać tej bazowej, a do zwracanego wyniku przypisać dowolną wartość (tak jak to tu opisują). Nie wiem jednak czy to spełnia twoją definicję "ingerencji w treść metody" ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1