[Home](../) › [Blog](../blog) › The Decorator Pattern in Modern C# [2024]

# The Decorator Pattern in Modern C# [2024]

> Want to extend the functionality of software components without boilerplate code? The decorator pattern is your solution.

Published: 2024-05-20 · Author: Metalama Team · Canonical: https://postsharp.net/blog/decorator-pattern

The Decorator design pattern allows software developers to extend the functionalities of a component without altering its code. This article explores the primary techniques for implementing the decorator pattern in modern .NET while adhering to the Single Responsibility Principle (SRP) and avoiding boilerplate code.

## When to use a Decorator pattern in C#

The Decorator pattern is useful when you want to add behavior to an existing component but either *cannot* or *do not want* to modify the source code. This is typically done to adhere to the single responsibility principle (SRP) to keep our code clean, readable, and maintainable.

Some real-world use cases for the decorator design pattern include:

- **Execution policies**, such as exception handling, retrying, or caching, which help improve the performance and reliability of your apps.
- **Observability**, for instance, by adding logging to all calls to an external component.
- **User Interface**, like adding a scrollbar to a large textbox. Another example is the [Adorner](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/controls/adorners-overview) concept in WPF.
- **Streams**, with features such as buffering, encryption, or compression.

## What is the Decorator pattern

A Decorator is essentially a wrapper that implements the same contract as the entity it's wrapping. We are intentionally using the vague term _contract_. As we will see in this article, it can mean two things: a C# interface if we implement a _type decorator_, or a method signature if we implement a _method decorator_. In both cases, the caller does not need to know that it is talking to a decorator rather than the final implementation. The pattern is recursive: we can add a decorator to a decorator, creating a chain of responsibility.

For instance, instead of just calling an unreliable service, we may want to retry a couple of times upon transient failure, and finally assign a unique ID to each exception, log it, and wrap the exception. We can represent the chain as follows:

```mermaid
flowchart LR

CALLER --> Log[Log and Wrap Exception] --> Retry[Retry Transient Failure]  --> SERVICE

```

In this article, we will explore two kinds of decorators: [type decorators](#type-decorator) and [method decorators](#method-decorator).

## The classic Type Decorator pattern

The classic _type decorator_ pattern is a purely object-oriented variant of the decorator pattern that relies on type _interfaces_.

To illustrate the idea, let's say we want to build a simple messaging app. We'd need a component that handles sending and receiving messages. This component implements the `IMessenger` interface and is implemented in a third-party library.

```csharp
public interface IMessenger
{
    void Send( Message message );

    public Message Receive();
}
```

We are using the `IMessenger` service from a client class:

```csharp
public class Client( IMessenger messenger )
{
    public void Greet()
    {
        messenger.Send( new Message( "Hello, world" ) );
        Console.WriteLine( "--> " + messenger.Receive().Text );
    }
}
```

We are instantiating the `Client` class from `Program.cs`:

```csharp
var messenger = new Messenger();
var client = new Client( messenger );
client.Greet();
```

That all works nicely on our development environment. However, as soon as we move things to production, we realize that the messenger service is unreliable and occasionally causes our app to crash. Since we don't own the source code of the `IMessenger` implementation, we cannot simply add the logic we need to each method.

How does the Decorator pattern help us tackle this problem?

Take a look at our design using the type decorator pattern in the class diagram below. Along with the decorators for error handling and retrying, we've introduced the `MessengerDecorator` abstract class that holds the wrapped `IMessenger` object, making it easier to implement individual decorators.

```mermaid
classDiagram
    direction TD

    class IMessenger {
        <<interface>>
        Send(Message)
        Receive() Message
    }

    class Messenger {
        Send(Message)
        Receive() Message
    }

    IMessenger <|-- Messenger

    class MessengerDecorator {
        <<abstract>>
        Underlying: IMessenger
        Send(Message)
        Receive() Message
    }

    IMessenger <|-- MessengerDecorator
    IMessenger --o MessengerDecorator

    class ExceptionReportingMessenger {
        Send(Message)
        Receive() Message
    }

    MessengerDecorator <|-- ExceptionReportingMessenger

    class RetryingMessenger {
        Send(Message)
        Receive() Message
    }

    MessengerDecorator <|-- RetryingMessenger

    class LoggingMessenger {
        Send(Message)
        Receive() Message
    }

    MessengerDecorator <|-- LoggingMessenger

```

Here is the implementation of the `ExceptionReportingMessenger` class:

```csharp
public class ExceptionReportingMessenger : MessengerDecorator
{
    private readonly IExceptionReportingService _reportingService;

    public ExceptionReportingMessenger(
        IMessenger underlying,
        IExceptionReportingService reportingService ) :
        base( underlying )
    {
        this._reportingService = reportingService;
    }

    public override void Send( Message message )
    {
        try
        {
            this.Underlying.Send( message );
        }
        catch ( Exception e )
        {
            this._reportingService.ReportException( "Failed to send message", e );

            throw;
        }
    }

    public override Message Receive()
    {
        try
        {
            return this.Underlying.Receive();
        }
        catch ( Exception e )
        {
            this._reportingService.ReportException( "Failed to receive message", e );

            throw;
        }
    }

}
```

The `RetryingMessenger` messenger is very similar.

Now, instead of passing the original `Messenger` component to the `Client` class, we are wrapping the `Messenger` into `RetryingMessenger`, then into an `ExceptionReportingMessenger`. This is finally the `ExceptionReportingMessenger` that we pass to the `Client`

```csharp
var originalMessenger = new Messenger();

var retryingMessenger = new ExceptionReportingMessenger(
    new RetryingMessenger( originalMessenger ),
    new ExceptionReportingService() );

var clientUsingDecorator = new Client( retryingMessenger );
clientUsingDecorator.Greet();
```

When the program calls `Client.Greet`, the control flow is the following:

```mermaid
flowchart LR

Client --> ExceptionReportingMessenger --> RetryingMessenger --> Messenger
```

The full source code of examples in this article is available on [GitHub](https://github.com/postsharp/TimelessDotNetEngineer/tree/main/src/patterns/decorator).

## Using type decorators with dependency injection

Obviously, in any modern C# application, you would not instantiate the components manually as in the examples above, but you would let dependency injection do the job.

If you are using .NET Core's [IServiceCollection](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection), there is a nice library called [Scrutor](https://github.com/khellang/Scrutor#decoration) that can easily wrap a service with a decorator.

For example, this is how to apply the decorators by using Scrutor. Note the calls to the `Decorate` methods: they are defined by Scrutor.

```csharp
var services = new ServiceCollection()
    .AddSingleton<IExceptionReportingService, ExceptionReportingService>()
    .AddSingleton<IMessenger, Messenger>()
    .AddSingleton<Client>()
    .Decorate<IMessenger, RetryingMessenger>()
    .Decorate<IMessenger, ExceptionReportingMessenger>()
    .BuildServiceProvider();

var client = services.GetRequiredService<Client>();
client.Greet();
```

Many dependency injection frameworks have built-in support for decorators. See for instance how [Autofac handles this problem](https://autofac.readthedocs.io/en/latest/advanced/adapters-decorators#decorators),

## The Abstract Type Decorator pattern

At first glance, our solution design seems perfect. But when we dig into the implementation of the decorators, we notice that the exception handling would be duplicated across other methods. Here, we're violating the Don't Repeat Yourself principle. The code is now harder to maintain than before because any change to the error handling must be made in each method of `ExceptionReportingMessenger` and any decorator of another type decorator.

We will now see how to improve the Type Decorator pattern to make the decorator logic more reusable.

Let's use the word _policy_ to designate the logic that we wrap a method call with. Policies can be abstracted away and encapsulated in a reusable way. In the following diagram, we have represented policies as an interface.

```mermaid

classDiagram
    direction TD

    class IMessenger {
        <<interface>>
        Send(Message)
        Receive() Message
    }

    class IPolicy {
        <<delegate>>
        Invoke(underlying)
    }

    RetryPolicy <|-- IPolicy
    ReportExceptionPolicy <|-- IPolicy

    class AbstractDecorator {
        <<abstract>>
        Policy:Policy
        Invoke(underlying)
    }

    AbstractDecorator ..> IPolicy

    class Messenger {
        Send(Message)
        Receive() Message
    }

    IMessenger <|-- Messenger

    class MessengerDecorator {
        Underlying: IMessenger
        Send(Message)
        Receive() Message
    }

    AbstractDecorator <|-- MessengerDecorator
    IMessenger <|-- MessengerDecorator

```

Here is the exception-reporting policy:

```csharp
public class ReportExceptionPolicy( IExceptionReportingService reportingService ) : IPolicy
{
    public T Invoke<T>( Func<T> func )
    {
        try
        {
            return func();
        }
        catch ( Exception e )
        {
            reportingService.ReportException( "Failed to send message", e );

            throw;
        }
    }
}
```

We then define an `AbstractDecorator`, an abstract class that can be used as a base for any decorator:

```csharp
public abstract class AbstractDecorator( IPolicy policy )
{
    protected T Invoke<T>( Func<T> func ) => policy.Invoke( func );

    protected void Invoke( Action action )
        => policy.Invoke<object?>(
            () =>
            {
                action();

                return null!;
            } );
}
```

In practice, you'll also need to implement `async` versions of the `Invoke` methods in both `IPolicy` and `AbstractDecorators`.

With this setup, all the `MessengerDecorator` has to do is wrap method implementations with calls to the `Invoke` methods of `AbstractDecorator`:

```csharp
public class MessengerDecorator( IMessenger underlying, IPolicy policy ) :
    AbstractDecorator( policy ), IMessenger
{
    public void Send( Message message ) => this.Invoke( () => underlying.Send( message ) );

    public Message Receive() => this.Invoke( underlying.Receive );
}
```

Note that this decorator is now abstracted from any policy. The only repetitive code is now in calling the `Invoke` method.

Finally, we wire the service collection using Scrutor's `Decorate` method by supplying one of the `Policies` to the `MessengerDecorator` class:

```csharp
var services = new ServiceCollection()
    .AddSingleton<IExceptionReportingService, ExceptionReportingService>()
    .AddSingleton<IMessenger, Messenger>()
    .Decorate<IMessenger>(
        ( inner, _ ) => new MessengerDecorator(
            inner,
            new RetryPolicy() ) )
    .Decorate<IMessenger>(
        ( inner, serviceProvider ) => new MessengerDecorator(
            inner,
            new ReportExceptionPolicy(
                serviceProvider.GetRequiredService<IExceptionReportingService>() ) ) )
    .BuildServiceProvider();

var client = services.GetRequiredService<Client>();
client.Greet();
```

We've now consolidated error-handling logic in one place.

The control flow now becomes:

```mermaid
flowchart LR

Client --> MessengerAnnotator1[MessengerAnnotator 1] --> ReportExceptionPolicy --> MessengerAnnotator2[MessengerAnnotator 2] --> RetryPolicy --> Messenger
```

## Generating Type Decorators automatically

There's still repetitive code in the `MessengerDecorator` class. Arguably, `MessengerDecorator` is _purely_ boilerplate and should ideally be removed from your codebase. There are two ways to generate this class:

* At _run time_, using an approach known as _dynamic proxies_, or
* At _compile time_, using source generators.

In this article, we will only explore the first solution.

The principle behind dynamic proxies is to generate the decorator class at run time, when the application initializes. Among the few libraries that implement this feature, the most popular is [Castle DynamicProxy](https://github.com/castleproject/Core/blob/master/docs/dynamicproxy.md). The concept of _policy_ developed earlier maps to Castle's `IInterceptor` interface. Here is the implementation of the retry policy as a Castle interceptor. Notice the similarity to the `RetryPolicy` class in the example above.

```csharp
internal class RetryInterceptor( int retryAttempts = 3, double retryDelay = 1000 ) : IInterceptor
{
    public void Intercept( IInvocation invocation )
    {
        for ( var i = 0;; i++ )
        {
            try
            {
                invocation.Proceed();
            }
            catch ( Exception ) when ( i < retryAttempts )
            {
                var delay = retryDelay * Math.Pow( 2, i );

                Console.WriteLine(
                    "Failed to receive message. " +
                    $"Retrying in {delay / 1000} seconds... ({i + 1}/{retryAttempts})" );

                Thread.Sleep( (int) delay );
            }
        }
    }
}
```

As promised, there's no longer a need for any decorator code since Castle has implemented it.

We can now proceed to the startup sequence of our application. We will need a `ProxyGenerator`:

```csharp
var proxyGenerator = new ProxyGenerator();
```

We can now use the `proxyGenerator.CreateInterfaceProxyWithTarget` method to create the proxy class, and supply the two interceptors implementing our policies:

```csharp
var services = new ServiceCollection()
    .AddSingleton<IExceptionReportingService, ExceptionReportingService>()
    .AddSingleton<IMessenger, Messenger>()
    .Decorate<IMessenger>(
        ( inner, _ ) => new MessengerDecorator(
            inner,
            new RetryPolicy() ) )
    .Decorate<IMessenger>(
        ( inner, serviceProvider ) => new MessengerDecorator(
            inner,
            new ReportExceptionPolicy(
                serviceProvider.GetRequiredService<IExceptionReportingService>() ) ) )
    .BuildServiceProvider();

var client = services.GetRequiredService<Client>();
client.Greet();
```

## The Method Decorator Pattern

So far, we've discussed techniques that help replace a type with another type implementing the same interface, but providing additional services. The principal benefits of this approach are:
- It's purely object-oriented,
- It works with code you don't own,
- It's composable at run time.

However, there's a significant drawback: it only works if you can inject yourself into the communication between the caller and the service -- typically through an interface, although the same could be achieved using `abstract` or `virtual` methods. If you appreciate the benefits of being able to decorate a method with any behavior, it's a pity to have to limit yourself so much. Even worse: you may be tempted to split your application into more smaller components to benefit from decorators. This is a case of _framework dictatorship_ and it should be avoided.

An alternative to the type decorator pattern is the _method decorator_. As its name suggests, method decorators target a single method and not the entire type. Method decorators are commonly used in dynamic languages such as [Python](https://www.programiz.com/python-programming/decorator). They aren't directly supported by C#, but some toolkits like [Metalama](https://www.postsharp.net/metalama) make it possible.

The idea of C# method decorators is to move the logic of the policy to a special kind of [custom attribute](https://learn.microsoft.com/en-us/dotnet/standard/attributes/) called an *aspect*, which could be compared to code templates. Unlike other custom attributes, [aspects](https://www.postsharp.net/solutions/aspect-oriented-programming) are applied to the code during compilation. Since this approach is compile-time, we're not limited to the limitations of the .NET runtime, namely we're not limited to virtual or interface methods, but we can intercept anything (including static private fields, if you ask).

Here is the Metalama version of the retry policy:

```csharp
internal class RetryAttribute : OverrideMethodAspect
{
    public int Attempts { get; set; } = 3;

    public double Delay { get; set; } = 1000;

    public override dynamic? OverrideMethod()
    {
        for ( var i = 0;; i++ )
        {
            try
            {
                return meta.Proceed();
            }
            catch ( Exception e ) when ( i < this.Attempts )
            {
                var delay = this.Delay * Math.Pow( 2, i + 1 );

                Console.WriteLine(
                    $"Method {meta.Target.Method.DeclaringType.Name}.{meta.Target.Method} has failed "
                    +
                    $" on {e.GetType().Name}. Retrying in {delay / 1000} seconds... ({i + 1}/{this.Attempts})" );

                Thread.Sleep( (int) delay );
            }
        }
    }

    // TODO: Implement OverrideMethodAsync and call Task.Delay instead of Thread.Sleep.
}
```

To add the policy to a method, apply it as a custom attribute:

```csharp
public partial class Messenger
{
    private int _receiveCount;
    private int _sendCount;

    [Retry]
    [ReportExceptions]
    public void Send( Message message )
    {
        Console.WriteLine( "Sending message..." );

        // Simulate unreliable message sending
        if ( ++this._sendCount % 3 == 0 )
        {
            Console.WriteLine( "Message sent successfully." );
        }
        else
        {
            throw new IOException( "Failed to send message." );
        }
    }

    [Retry]
    [ReportExceptions]
    public Message Receive()
    {
        Console.WriteLine( "Receiving message..." );

        // Simulate unreliable message receiving
        if ( ++this._receiveCount % 3 == 0 )
        {
            Console.WriteLine( "Message received successfully." );

            return new Message( "Hi!" );
        }

        throw new IOException( "Failed to receive message." );
    }
}
```

To further improve maintainability, toolkits like Metalama facilitate the bulk application of aspects, eliminating the need for developers to manually specify where each aspect should be used. For instance, we can stipulate that all public methods within a specific namespace should have exception reporting. Consequently, when new methods are added to this namespace, the exception-reporting aspect is automatically applied. This approach not only enhances the readability and maintainability of the codebase but also simplifies scalability. In Metalama, this is achieved using fabrics. The following example demonstrates how to add exception reporting to all public methods in a project:

```csharp
internal class AddExceptionReportingToPublicMethodsFabric : ProjectFabric
{
    public override void AmendProject( IProjectAmender amender )
    {
        amender
            .SelectMany( t => t.AllTypes )
            .SelectMany( t => t.Methods )
            .Where( m => m.Accessibility == Accessibility.Public )
            .AddAspectIfEligible<ReportExceptionsAttribute>();
    }
}
```

## Summary

Decorators are an effective way to maintain clean code and uphold the single responsibility principle. Opt for type decorators when you don't own the code that you wish to enhance with new behaviors, or when you need to dynamically add behaviors at runtime. Use method decorators (aspect-oriented) when you own the source code and aim to adhere to the Single Responsibility Principle.

---

## Site navigation

- Index of the whole site: [llms.txt](../llms.txt)

