4

What I have is:

class ABC
{
    public void MethodA()
    {
        Console.WriteLine("In method A");
    }
    public void MethodB()
    {
        Console.WriteLine("In method B");
    }
    public void MethodC()
    {
        Console.WriteLine("In method C");
    }
}

class PQR
{
    public void MethodP()
    {
        Console.WriteLine("In method P");
    }
    public void MethodQ()
    {
        Console.WriteLine("In method Q");
    }
    public void MethodR()
    {
        Console.WriteLine("In method R");
    }
}

What I want to achieve is:

Call (or maybe inject using any DI frameworks)

MethodP() in MethodA(), MethodQ() in MethodB(), MethodR() in MethodC(),

But without extending Class PQR on Class ABC or vice versa. Or without modifying Class ABC, I can Modify Class PQR. I did checked some of the existing DI frameworks like Prism, Autofac, Unity but to use them I have to modify Class ABC (Adding some attributes, extending to some interfaces etc.), which I don't want to do.

How can I achieve this?

UPDATE 1: Class ABC and class PQR don't have any super class/interface in common.

meJustAndrew
  • 6,011
  • 8
  • 50
  • 76
Yogesh
  • 1,565
  • 1
  • 19
  • 46
  • If using `Castle Windsor` use [Interceptors](http://stackoverflow.com/documentation/castle-windsor/6663/interceptors/22685/creating-custom-interceptors#t=201610251357120284495) – Gilad Green Oct 25 '16 at 13:57
  • Do `ABC` and `PQR` implement or extend the same interface/base class? If so, you should be just fine wrapping `ABC` inside `PQR` and injecting the instance of `PQR` instead of `ABC`. – rory.ap Oct 25 '16 at 13:58
  • Possible duplicate of [Run a method before all methods of a class](http://stackoverflow.com/questions/9192709/run-a-method-before-all-methods-of-a-class) – GSerg Oct 25 '16 at 13:59
  • @rory.ap No they don't. – Yogesh Oct 25 '16 at 13:59
  • Possible duplicate of [Call a method each time before any other method is called](http://stackoverflow.com/q/1969089/11683) – GSerg Oct 25 '16 at 14:00
  • Well, what *do* they implement? That's a necessary part of DI... – rory.ap Oct 25 '16 at 14:00
  • 3
    You don't really want DI, you want something like PostSharp. – DavidG Oct 25 '16 at 14:00
  • Possible duplicate of [How to call a method implicitly after every method call?](http://stackoverflow.com/q/33075283/11683) – GSerg Oct 25 '16 at 14:00
  • 2
    @GSerg Pick one please! I'll hammer it... – DavidG Oct 25 '16 at 14:02
  • @rory.ap they don't have any class of interface in comman – Yogesh Oct 25 '16 at 14:02
  • @DavidG I'm not sure which one is better. They all contain different relevant links and none of them appears to be good enough on its own. – GSerg Oct 25 '16 at 14:06
  • @GSerg -- That seems like an indication to me that this question is *not* a dupe. – rory.ap Oct 25 '16 at 14:10
  • 2
    @rory.ap I think it's more likely that the answers are not good enough. Perhaps a candidate for a canonical question/answer. – DavidG Oct 25 '16 at 14:17
  • is class `ABC` in a 3rd party dll you can't modify? – Scott Chamberlain Oct 25 '16 at 14:36
  • @ScottChamberlain No, It's not third party dll. But I have 50+ such classes like ABC. – Yogesh Oct 25 '16 at 14:42
  • Possible duplicate of [How do I intercept a method call in C#?](http://stackoverflow.com/questions/25803/how-do-i-intercept-a-method-call-in-c) – Clint Oct 25 '16 at 14:59

4 Answers4

4

The general design pattern for this is the Decorator Pattern. You can do this by defining an interface IABC for ABC:

interface IAbc {
    void MethodA();
    void MethodB();
    void MethodC();
}

Now you can define a decorator for IABC that is able to 'intercept' calls to ABC and calls PQR before calling ABC:

class AbcToPqrDecorator : IAbc
{
    private readonly PQR pqr;
    private readonly IAbc decorated;

    public AbcToPqrDecorator(PQR pqr, IAbc decorated) {
        this.pqr = pqr;
        this.decorated = decorated;
    }

    public void MethodA() {
        pqr.MethodP();
        decorated.MethodA();
    }
    public void MethodB() {
        pqr.MethodQ();
        decorated.MethodB();
    }

    public void MethodC() {
        pqr.MethodR();
        decorated.MethodC();
    }
}

You can create the object graph as follows:

IAbc abc = new AbcToPqrDecorator(new PQR(), new ABC());

Important note: in case you find the use of decorators leads to a lot of overhead (because you are required to define many decorator implementations with the same behavior but for different interfaces), this is an indication that you are violating SOLID and missing a common abstraction. Your comment indicates that this is indeed the case:

I have 50+ such classes like ABC.

As an example of how to design your system, this article describes how you can design a part of your system in such way that it becomes trivial to wrap operations like MethodA(), MethodB() and MethodC() with one generic decorator that only needs to be defined once.

Steven
  • 166,672
  • 24
  • 332
  • 435
  • @meJustAndrew woops, that's a typo. Fixed it. – Steven Oct 25 '16 at 14:33
  • 1
    Great, now is a valid answer! but you just changed the rules (modified the class `ABC`). – meJustAndrew Oct 25 '16 at 14:57
  • @Steven, can you elaborate on the SOLID violation part? In specific, about the decorators you mention. Do you mean *similar* decorators for different interfaces? – Yacoub Massad Oct 25 '16 at 15:00
  • @YacoubMassad Since ABC implements 3 methods, it is unlikely that all consumers use all 3 methods. In case they don't, ABC is violating the Interface Segregation Principle. – Steven Oct 25 '16 at 15:12
  • @YacoubMassad in case you need many decorators with the same behavior, but for different interfaces, chances are high that some general abstraction is missing. – Steven Oct 25 '16 at 15:13
  • @Steven, I agree with you about the potential ISP violation. Regarding the similar decorators, that is a DRY violation, but not necessarily a SOLID violation. It can be solved with Command/Query separation with a common abstraction as you mention, but can also be solved using other AOP options. [This article](http://www.dotnetcurry.com/patterns-practices/1305/aspect-oriented-programming-aop-csharp-using-solid) discusses some options for doing AOP. – Yacoub Massad Oct 25 '16 at 15:18
  • @YacoubMassad Yes, you can solve this problem with code weaving or interception as well, but my general point is that these techniques only shallowly solve your problems, while other problems remain. I advocate SOLID and CQRS-like techniques, because they solve these problems effectively and elegantly at the root and even even allow solving these problems without any tools. – Steven Oct 25 '16 at 15:24
  • @Steven, regarding compile-time code weaving, the current tools (like PostSharp) apply aspects at classes and not objects AFAIK. This makes it hard to compose/apply aspects to different objects of the same class differently. I think that is a problem. However, other AOP options (e.g. via dynamic proxy generators, via T4 design-time decorator generation) allow you to apply aspects easily to objects in the Composition Root. I speak about this in the article I mentioned. In what way would the CQRS-like solution (although it is a good solution) be better than these other solutions? – Yacoub Massad Oct 25 '16 at 16:11
2

One method how to handle this kind of stuff is Castle.DynamicProxy. There are drawbacks - if you don't implement class instead of interface, methods needs to be virtual in order for interception to work:

//Install-Package Castle.DynamicProxy

public class Interceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Console.Out.WriteLine("Intercepting: " + invocation.Method.Name);
        invocation.Proceed();
    }
}

public class ABC
{
    public virtual void MethodA()
    {
        Console.WriteLine("In method A");
    }
    public void MethodB()
    {
        Console.WriteLine("In method B");
    }
}

Usage:

var generator = new ProxyGenerator();
var abc = generator.CreateClassProxy<ABC>(new Interceptor());
// "Intercepting: MethodA"
// "In method A"
abc.MethodA();
// oops - not virtual method - no interception
// "In method B"
abc.MethodB();

PostSharp is much more powerful, and the AOP magic happens after the build (so it is more performant), unfortunatelly it is not free.

Ondrej Svejdar
  • 21,349
  • 5
  • 54
  • 89
2

I am working on NConcern .NET AOP Framework, a new open source AOP framework working at runtime whithout any factory/proxy pattern and reflection performance cost. It seems to be able to handle what you need.

Please take a look on.

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

Usage

static public void main(string[] args)
{
    //MonkeyPatch.Patch(typeof(ABC).GetMethod("A"), typeof(PQR).GetMethod("P"));
    MonkeyPatch.Patch(Metadata<ABC>.Method(_ABC => _ABC.A()), Metadata<PQR>.Method(_PQR=> _PQR.P()));

    //MonkeyPatch.Patch(typeof(ABC).GetMethod("B"), typeof(PQR).GetMethod("Q"));
    MonkeyPatch.Patch(Metadata<ABC>.Method(_ABC => _ABC.B()), Metadata<PQR>.Method(_PQR=> _PQR.Q()));

    //MonkeyPatch.Patch(typeof(ABC).GetMethod("C"), typeof(PQR).GetMethod("R"));
    MonkeyPatch.Patch(Metadata<ABC>.Method(_ABC => _ABC.C()), Metadata<PQR>.Method(_PQR=> _PQR.R())); 
}
Tony THONG
  • 772
  • 5
  • 11
1

I really see your PQR class as holding an event instead of methods, since you want to inject a method inside another method:

class PQR
{
    public event EventHandler MethodP;
    public event EventHandler MethodQ;
    public event EventHandler MethodR;

    public PQR()
    {
        MethodP += delegate { MethodP1(); };
        MethodQ += delegate { MethodQ1(); };
        MethodR += delegate { MethodR1(); };
    }

    private void MethodP1()
    {
        Console.WriteLine("In method P");
    }
    private void MethodQ1()
    {
        Console.WriteLine("In method Q");
    }
    private void MethodR1()
    {
        Console.WriteLine("In method R");
    }
}

then you can simply add methods to the events:

var abc =  new ABC();
var pqr = new PQR();
pqr.MethodP += delegate { abc.MethodA(); };
pqr.MethodQ += delegate { abc.MethodB(); };
pqr.MethodR += delegate { abc.MethodC(); };

And this way you can let your ABC class untouched.

meJustAndrew
  • 6,011
  • 8
  • 50
  • 76
  • But this way MethodA will be called inside MethodB. And I want it to happen other way. – Yogesh Oct 25 '16 at 15:08
  • nope @Yogesh `MethodA` and `MethodB` are still completly separate. Tell me why do you think they are related to each other? – meJustAndrew Oct 25 '16 at 15:09
  • oh sorry, I mean MethodP() instead of MethodB(). – Yogesh Oct 25 '16 at 15:11
  • 1
    @Yogesh okay, I have updated my answer in order to show you that `MethodP()` and `MethodA()` are still separate. You can not call `MethodP` in `MethodA` without modifying the `ABC` class. – meJustAndrew Oct 25 '16 at 15:14