views:

260

answers:

10

Hi, I need to extend the behavior of an instance, but I don't have access to the original source code of that instance. For example:

/* I don't have the source code for this class, only the runtime instance */
Class AB
{
  public void execute();
}

in my code I would to intercept every call to execute, compute some sutff and then call the original execute, something like

/* This is how I would like to modify the method invokation */
SomeType m_OrgExecute;

{
    AB a = new AB();
    m_OrgExecute = GetByReflection( a.execute );
    a.execute = MyExecute;
}

void MyExecute()
{
    System.Console.Writeln( "In MyExecute" );
    m_OrgExecute();
}

Is that possible?

Does anyone have a solution for this problem?

A: 

if AB is not sealed, then you can inherit the class and override the method. in this case with new

class ABChild : AB {
    public new void execute() {
        System.Console.Writeln( "In MyExecute" );
    }
}

According to the comments, you should invocate this new method, within an ABChild class:

void Invoke() {
    ABChild a = new ABChild();
    a.execute();
}

Hope it helps !!

Jhonny D. Cano -Leftware-
This will not work if the caller of execute() is using an AB. It will call the original method on AB unless you cast it to ABChild.
jkohlhepp
+1  A: 

I would take a look at PostSharp. It can "rewire" existing compiled assemblies to add the kind of before and after processing that you are looking for. I'm not 100% sure it will address your need but it very well might.

http://www.sharpcrafters.com/aop.net

jkohlhepp
This requires the assembly to be attributed, since that's how post sharp determines what to rewrite.
Reed Copsey
@Reed: You can use [assembly:] level attributes with PostSharp. That said, those probably have to go in the AssemblyInfo.cs of the assembly.
TrueWill
+2  A: 

There is no way to do this directly via reflection, etc.

In order to have your own code injected like this, you'll need to create a modified version of their assembly, and use some form of code injection. You cannot just "change a method" of an arbitrary assembly at runtime.

Reed Copsey
A: 

You could always inherit from the class and override the execute() method (if the class isn't sealed, and the method isn't private at least.)

Pierreten
A: 

You could use a wrapper class:

Class ABWrapper
{
  private AB m_AB;

  ABWrapper( AB ab )
  {
    m_AB = new AB();
  }

  public void execute()
  {
    // Do your stuff, then call original method
    m_AB.execute();
  }
}

This is a good approach when AB implements an interface (you did not mention that, though). In this case ABWrapper should implement the same interface. When you use a factory or even dependency injection to create your AB instances you can replace them there easily with your wrapper.

tanascius
+2  A: 

It looks like you want the Decorator pattern.

class AB
{
   public void execute() {...}
}

class FlaviosABDecorator : AB
{
   AB decoratoredAB;

   public FlaviosABDecorator (AB decorated)
   {
       this.decoratedAB = decorated;
   }

   public void execute()
   {
       FlaviosExecute();  //execute your code first...
       decoratedAB.execute();
   }

   void FlaviosExecute() {...}
}

You'd then have to modify the code where the AB object is used.

//original code
//AB someAB = new AB();

//new code
AB originalAB = new AB();
AB someAB = new FlaviosABDecorotor(originalAB);

/* now the following code "just works" but adds your method call */
Austin Salonen
+1  A: 

Because I favor composition over inheritance (and because inheritance might not be an option if the class is sealed), I would wrap AB in my own class called FlaviosAB like so...

public class FlaviosAB
{
    private AB _passThrough;
    public FlaviosAB(){
        _passThrough = new AB();
    }

    public void execute()
    {
        //Your code...
        Console.WriteLine("In My Execute");
        //Then call the passThrough's execute.
        _passThrough.execute();
    }
}
Jason Punyon
This gets past any problem of the original class being sealed. Depending on how many other public methods on AB the caller needs, more might need to be expressed through the wrapper.
John K
@jdk: More might be necessary, but it wasn't specified in the question so I didn't include it :) Also, your gravatar looks just like my tattoo :)
Jason Punyon
@Jason Punyon: If your tattoo image was published on the Internet then I stole it as my Gravatar. I can only give +1 to your answer once, otherwise I'd provide a second for the cool tattoo! :)
John K
+1  A: 

You could use a dynamic proxy such as Castle Proxy.

Todd Stout
A: 

You can implement a dynamic proxy. Find more information here

Basically you are extending the base class and overriding some methods with yours. Now you need to re-assign the object being called by an instance of yours and all the calls will pass through your object first.

Cristian
A: 

Maybe one of the Aspect-Oriented Programming (AOP) solutions from this Stackoverflow thread will come in handy...

John K