How to extend a method at runtime?
Asked Answered
L

1

0

Here is the class:

class Foo
{
    private void Boo()
    {
        // Body...
    }

    // Other members...
}

What I need is:

  • Create a Foo2 class at runtime which has a copy of all Foo class members.
  • In Foo2 class replace Boo method by Boo2 method that has its content alternated to some extent.
  • Create an instance of Foo2 and invoke Boo2.

Thank you for help.

Leonardaleonardi answered 12/12, 2013 at 15:0 Comment(13)
Would it be possible to change the signature of Boo to protected virtual?Demodena
Could you explain why you are doing this? I get the feeling there is a better way...Stile
@Demodena Eventually yes - it could be possible to change the signature of Boo to protected virtual, however I'm actually looking for solution that will allow to not touch the original code.Fleda
Would partial methods solve your problem? msdn.microsoft.com/en-us/library/6b0scde8.aspxGrinnell
@RichardEv that was my initial thought as well, however, I would like to hear the justification first.Stile
@Stile I'm writing a test that uses Foo. Test is finished when Boo is finished. I need to put some notification into Boo method in order to stop my test.Fleda
Yes you can do it,but it's little complicated. Check TypeBuilder class and example is here: msdn.microsoft.com/tr-tr/library/…Missal
@RyszardDżegan extending behaviour is probably best served using AOP and there are tools out there for that. Partials wouldn't work in this case because you want to extend not implement the full method.Stile
@RichardEv Boo is kind of spaghetti code. There is an if inside. My test is finished within that if. I don't want to do any refactoring of that method. Just make a copy of it, find that if inside that copy and attach some notification after that. The key thing here is to not amend original Foo nor Boo.Fleda
@RyszardDżegan: That usually calls for a refactored class. Boo and the calling method should be in different classes so you can mock boo the usual way. Alternatively make Boo internal protected virtual (assuming you have InternalsVisibleTo set for your test assembly) and create a derived class Foo2 in your test where you can override Boo.Demodena
@Selman22 That sounds good. Could you please add an example that meets my three points to give me a quick start point?Fleda
@Stile I know that with such tools I'm able to add actions to certain points within my method body. However, can I also have finer control over where I set such extensions?Fleda
i use it for create a new assembly and class at runtime for example,but you want copy a method body,i'm not sure i can do that,in microsoft example they emit CLR opcodes into method body,so to do this i guess you must have CLR knowledge or you can open your current assembly with ILDASM.exe and copy your method Opcodes and emit them with c# code.. as i said it's really complicatedMissal
P
1

You can do it at runtime using a .NET AOP Framework event if it is not the the main purpose of this kind of framework.

I actively work on a new one which can handle it event if your method is not virtual.

You can take a look on NConcern .NET runtime AOP Framework

The monkey patch "aspect" :

public class MonkeyPatch : IAspect
{
    static public void Patch(MethodInfo oldMethod, MethodInfo newMethod)
    {
        //update monkey patch dictionary
        MonkeyPatch.m_Dictionary[oldMethod] = newMethod;

        //release previous monkey patch for target method.
        Aspect.Release<MonkeyPatch>(oldMethod);

        //weave monkey patch for target method.
        Aspect.Weave<MonkeyPatch>(oldMethod);
    }

    static private Dictionary<MethodInfo, MethodInfo> m_Dictionary = new Dictionary<MethodInfo, MethodInfo>();

    public IEnumerable<IAdvice> Advise(MethodInfo method)
    {
        if (MonkeyPatch.m_Dictionary.ContainsKey(_Method))
        {
            yield return Advice(MonkeyPatch.m_Dictionary[_Method]);
        }
    }
}

Patch :

static public void main(string[] args)
{
    //create Boo2, a dynamic method with Boo signature.
    var boo2 = new DynamicMethod("Boo2", typeof(void), new Type[] { typeof(Foo) }, typeof(Foo), true);
    var body = boo2.GetILGenerator();

    //Fill your ILGenerator...
    body.Emit(OpCodes.Ret);

    //Apply the patch
    MonkeyPatch.Patch(typeof(Foo).GetMethod("Boo"), boo2);
}

in the second hand, if you just need to call something after the original call, you are in AOP aim and you can do it like that...

Observation Aspect :

public class Observation : IAspect
{
    static public void Observe(MethodInfo method, Action action)
    {
        //update observation dictionary
        Observation.m_Dictionary[method] = action;

        //release observation aspect for target method
        Aspect.Release<Observation>(method);

        //weave observation aspect for target method.
        Aspect.Weave<Observation>(method);
    }

    static private Dictionary<MethodInfo, Action> m_Dictionary = new Dictionary<MethodInfo, Action>;

    public IEnumerable<IAdvice> Advice(MethodInfo method)
    {
        if (Observation.m_Dictionary.ContainsKey(method))
        {
            yield return Advice.Basic.After(Observation.m_Dictionary[method]);
        }
    }
}

Use case :

static public void main(string[] args)
{
    Observation.Observe(typeof(Foo).GetMethod("Boo"), () => { /* paste here your notification code... */ });
}
Provide answered 9/12, 2016 at 13:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.