Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# intercepting methods and injecting method calls

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.

like image 365
Yogesh Avatar asked Oct 29 '25 14:10

Yogesh


2 Answers

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.

like image 54
Steven Avatar answered Nov 01 '25 05:11

Steven


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.

like image 31
Ondrej Svejdar Avatar answered Nov 01 '25 03:11

Ondrej Svejdar



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!