arrow-left arrow-right brightness-2 chevron-left chevron-right circle-half-full dots-horizontal facebook-box facebook loader magnify menu-down RSS star Twitter twitter GitHub white-balance-sunny window-close
Headers - attaching arbitrary properties to objects
1 min read

Headers - attaching arbitrary properties to objects

This is an old post and doesn't necessarily reflect my current thinking on a topic, and some links or images may not work. The text is preserved here for posterity.

On a project recently I had a marker interface like this:

public interface IMessage
{
}

I wanted the ability to attach arbitrary headers to a message - for example:

message.SetHeader("Expires", "2011-09-03 15:30:00");
message.SetHeader("CorrelationId", 45);

I didn't want SetHeader to be a method on the interface, so I created an extension method. But how can we store the arbitrary header values against the object instance?

My solution was to use ConditionalWeakTable. This allows properties to be stored against objects without preventing the object from being garbage collected.

The implementation looked something like this:

public static class MessageExtensions
{
    private static readonly ConditionalWeakTable<IMessage, HeaderCollection> headerMap = new ConditionalWeakTable<IMessage, HeaderCollection>();

    public static void SetHeader(this IMessage message, string header, string value)
    {
        var headers = GetHeaders(message);
        headers[header] = value;
    }

    public static string GetHeader(this IMessage message, string header)
    {
        var headers = message.GetHeaders();
        string value;
        headers.TryGetValue(header, out value);
        return value;
    }

    public static HeaderCollection GetHeaders(this IMessage message)
    {
        return headerMap.GetValue(message, x => new HeaderCollection());
    }
} 

I put the extension methods in the global namespace, so that they are available on any IMessage without the coder needing to include the namespace.

Here is a unit test to demonstrate the garbage collection continuing to work:

[TestMethod]
public void HeadersDoNotPreventGarbageCollection()
{
    WeakReference reference = null;

    new Action(() =>
    {
        var message1 = new MessageA();
        message1.SetHeader("Test", "Foo");

        reference = new WeakReference(message1, true);
        Assert.IsNotNull(reference.Target);
    })();

    GC.Collect();
    GC.WaitForPendingFinalizers();

    Assert.IsNull(reference.Target);
}

Enjoying these posts? Subscribe for more

Subscribe now
Already have an account? Sign in
Paul Stovell's Blog

Hello, I'm Paul Stovell

I'm a Brisbane-based software developer, and founder of Octopus Deploy, a DevOps automation software company. This is my personal blog where I write about my journey with Octopus and software development.

I write new blog posts about once a month. Subscribe and I'll send you an email when I publish something new.

Subscribe

Comments

You've successfully subscribed to Paul Stovell's Blog.
Success! Your account is fully activated, you now have access to all content.
Success! Your billing info is updated.