Как расширить метод во время выполнения?

Вот класс:

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

    // Other members...
}

Мне нужно:

  • Создайте класс Foo2 во время выполнения, который имеет копию всех членов класса Foo.
  • В классе Foo2 замените метод Boo на метод Boo2, содержимое которого несколько изменено.
  • Создайте экземпляр Foo2 и вызовите Boo2.

Спасибо за помощь.


person Ryszard Dżegan    schedule 12.12.2013    source источник
comment
Можно ли изменить подпись Бу на protected virtual?   -  person PMF    schedule 12.12.2013
comment
Не могли бы вы объяснить, почему вы это делаете? Я чувствую, что есть лучший способ...   -  person James    schedule 12.12.2013
comment
@PMF В конце концов да - можно было бы изменить подпись Бу на protected virtual, однако я на самом деле ищу решение, которое позволит не трогать исходный код.   -  person Ryszard Dżegan    schedule 12.12.2013
comment
Частичные методы решат вашу проблему? msdn.microsoft.com/en-us/library/6b0scde8.aspx   -  person Richard Ev    schedule 12.12.2013
comment
@RichardEv это тоже была моя первоначальная мысль, однако я хотел бы сначала услышать обоснование.   -  person James    schedule 12.12.2013
comment
@James Я пишу тест, в котором используется Foo. Тест завершен, когда Boo завершен. Мне нужно поместить уведомление в метод Boo, чтобы остановить мой тест.   -  person Ryszard Dżegan    schedule 12.12.2013
comment
Да, вы можете это сделать, но это немного сложно. Проверьте класс TypeBuilder и пример здесь: msdn.microsoft.com/tr-tr/library/   -  person Selman Genç    schedule 12.12.2013
comment
Расширение поведения @RyszardDżegan, вероятно, лучше всего обслуживается с помощью АОП, и для этого существуют инструменты. Частичные не будут работать в этом случае, потому что вы хотите расширить не реализовать полный метод.   -  person James    schedule 12.12.2013
comment
@RichardEv Boo — это что-то вроде спагетти-кода. Внутри находится if. Мой тест завершен в течение этого if. Я не хочу делать какой-либо рефакторинг этого метода. Просто сделайте его копию, найдите этот if внутри этой копии и после этого прикрепите уведомление. Главное здесь — не изменять исходные Foo и Boo.   -  person Ryszard Dżegan    schedule 12.12.2013
comment
@RyszardDżegan: Обычно это требует рефакторинга класса. Boo, а вызывающий метод должен быть в разных классах, чтобы вы могли издеваться над бу как обычно. В качестве альтернативы создайте Boo internal protected virtual (при условии, что для вашей тестовой сборки установлено значение InternalsVisibleTo) и создайте производный класс Foo2 в своем тесте, где вы можете переопределить Boo.   -  person PMF    schedule 12.12.2013
comment
@ Selman22 Звучит хорошо. Не могли бы вы добавить пример, который соответствует моим трем пунктам, чтобы дать мне быструю отправную точку?   -  person Ryszard Dżegan    schedule 12.12.2013
comment
@James Я знаю, что с помощью таких инструментов я могу добавлять действия в определенные точки в теле моего метода. Однако могу ли я также иметь более точный контроль над тем, где я устанавливаю такие расширения?   -  person Ryszard Dżegan    schedule 12.12.2013
comment
я использую его, например, для создания новой сборки и класса во время выполнения, но вы хотите скопировать тело метода, я не уверен, что смогу это сделать, в примере Microsoft они испускают коды операций CLR в тело метода, поэтому я думаю, чтобы сделать это у вас должно быть знание CLR, или вы можете открыть свою текущую сборку с помощью ILDASM.exe и скопировать свои коды операций метода и выдать их с кодом С#.. как я уже сказал, это действительно сложно   -  person Selman Genç    schedule 12.12.2013


Ответы (1)


Вы можете сделать это во время выполнения, используя событие .NET AOP Framework, если это не является основной целью этого типа фреймворка.

Я активно работаю над новым, который может обрабатывать события, если ваш метод не виртуальный.

Вы можете ознакомиться с средой выполнения NConcern .NET AOP Framework.

Патч обезьяны "аспект":

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]);
        }
    }
}

Пластырь :

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);
}

во-вторых, если вам просто нужно что-то вызвать после исходного вызова, вы находитесь в АОП-прицеле, и вы можете сделать это так...

Аспект наблюдения:

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]);
        }
    }
}

Вариант использования:

static public void main(string[] args)
{
    Observation.Observe(typeof(Foo).GetMethod("Boo"), () => { /* paste here your notification code... */ });
}
person Tony THONG    schedule 09.12.2016