Matthew Groves
Matthew Groves

Reputation: 26169

Can I use the decorator pattern to wrap a method body?

I have a bunch of methods with varying signatures. These methods interact with a fragile data connection, so we often use a helper class to perform retries/reconnects, etc. Like so:

MyHelper.PerformCall( () => { doStuffWithData(parameters...) });

And this works fine, but it can make the code a little cluttery. What I would prefer to do is decorate the methods that interact with the data connection like so:

[InteractsWithData]
protected string doStuffWithData(parameters...)
{
     // do stuff...
}

And then essentially, whenever doStuffWithData is called, the body of that method would be passed in as an Action to MyHelper.PerformCall(). How do I do this?

Upvotes: 30

Views: 31253

Answers (7)

Stefan Steinegger
Stefan Steinegger

Reputation: 64628

Without the use of code generation, you can't do much against it. You could probably make the syntax better.

But what about using an extension method?

class static MyHelper
{
  Wrap<T>(this object service, Action<T> action)
  {
    // check attribute and wrap call
  }

}

usage:

RawFoo foo = ...
foo.Wrap(x => x.doStuffWithData(parameters...));

This is trivial, but you can't make sure that Wrap had been used.

You could implement a generic decorator. This decorator would be used once to wrap the service and then you can't call it without wrapping.

class Decorator<T>
{
    private T implementor;

    Decorator(T implementor)
    {
      this.implementor = implementor;
    }

    void Perform<T>(Action<T> action)
    {
      // check attribute here to know if wrapping is needed
      if (interactsWithData)
      {
        MyHelper.PerformCall( () => { action(implementor) });
      }
      else
      {
        action(implementor);
      }
    }
}

static class DecoratorExtensions
{
    public static Decorator<T> CreateDecorator<T>(T service)
    {
      return new Decorator<T>(service);
    }
}

Usage:

// after wrapping, it can't be used the wrong way anymore.
ExtendedFoo foo = rawFoo.CreateDecorator();
foo.Perform(x => x.doStuffWithData(parameters...));

Upvotes: 4

dtb
dtb

Reputation: 217351

.NET Attributes are meta-data, not decorators / active components that automatically get invoked. There is no way to achieve this behaviour.

You could use attributes to implement decorators by putting the decorator code in the Attribute class and call the method with a helper method that invokes the method in the Attribute class using Reflection. But I'm not sure this would be a big improvement over just calling the "decorator-method" directly.

"Decorator-Attribute":

[AttributeUsage(AttributeTargets.Method)]
public class MyDecorator : Attribute
{
    public void PerformCall(Action action)
    {
       // invoke action (or not)
    }
}

Method:

[MyDecorator]
void MyMethod()
{
}

Usage:

InvokeWithDecorator(() => MyMethod());

Helper method:

void InvokeWithDecorator(Expression<Func<?>> expression)
{
    // complicated stuff to look up attribute using reflection
}

Have a look at frameworks for Aspect Oriented Programming in C#. These may offer what you want.

Upvotes: 21

Matthew Groves
Matthew Groves

Reputation: 26169

So, I just went to a AOP session this weekend, and here's a way to do it with PostSharp:

[Serializable]
public class MyAOPThing : MethodInterceptionAspect
{
    public override void OnInvoke(MethodInterceptionArgs args)
    {
        Console.WriteLine("OnInvoke! before");
        args.Proceed();
        Console.WriteLine("OnInvoke! after");
    }
}

And then decorate methods with [MyAOPThing]. Easy!

Upvotes: 22

Sijin
Sijin

Reputation: 4550

Seeing that you're willing to add a line of code to every method that needs this, why not just make the call to MyHelper from within the method itself, like this?

protected string doStuffWithData(parameters...)
{
     MyHelper.PerformCall( () => { doStuffWithDataCore(parameters...) });
}

private string doStuffWithDataCore(parameters...) {
    //Do stuff here
}

Upvotes: -1

JohnKeller
JohnKeller

Reputation: 419

It seems like what you want is similar to behavior of an IoC container or test runner framework, where it isn't actually executing from your assembly, but is running a dynamically emitted assembly built around your code. (Smarter folks than I have called this AOP in other answers)

So maybe in a stub for your app you could scan the other assemblies, build those emitted assemblies (which call MyHelper.PerformCall with the body of the decorated methods) then your program runs against the emitted code.

By no means would I start down the road of trying to write this without evaluating whether some existing AOP framework could accomplish what you need. HTH>

Upvotes: 1

Pontus Gagge
Pontus Gagge

Reputation: 17268

Check out aspect oriented frameworks. But be aware that while they hide complexity in each method, the existence of AoP features could make your program harder to maintain. It's a tradeoff.

Upvotes: 1

Dave Cluderay
Dave Cluderay

Reputation: 7426

This type of problem is pretty much what AOP (aspect oriented programming) aims to solve. Tools such as PostSharp can provide cross-cutting concerns by re-writing the compiled code. Scott Hanselman's podcast recently discussed AOP, so it might be worth having a listen.

Upvotes: 4

Related Questions