Fluent interfaces in C# – Generics

If you haven’t read the previous posts on the subject of fluent interfaces using C# – I suggest you do so now – I’ll wait…
  1. Introduction
  2. Extension Methods
  3. Method Chaining
Great! now you’re all ready for the forth post on the subject of using Generics:
Generics were added to version 2.0 of the C# language and the common language runtime (CLR). Generics introduce to the .NET Framework the concept of type parameters, which make it possible to design classes and methods that defer the specification of one or more types until the class or method is declared and instantiated by client code.
[Generics (C# Programming Guide)]

Using generics tor return specific type

If you’ve been programming in C# you’re probably seen generics in the past– either a type safe collection (i.e. List<T>) or written your own generic class/method.
Using Generics in your API is just like saying “of type X” as part of the interface – for example “create a fake/mock of type ILogger”:
// Typemock Isolator
var fakeLogger = Isolate.Fake.Instance<ILogger>();

// Telerik's JustMock
var fakeLogger = Mock.Create<ILogger>();

// NSubstitute 
var fakeLogger = Substitute.For<ILogger>();

Reading the code above you’ll notice something - even if you are not familiar with the topic of mocking frameworks you can still understand what each line does.

The reason that this API is used in mocking frameworks is that they all have something in common – they need to create a new instance of type X -  and so they get the type parameter as part of the method call a.k.a generics.

Using Generics to configure action(s) on specific type

If you’re familiar with .NET IoC you might recognize the following API:

Kernel.Register(
   Component.For<ICustomer>()
       .Named("customer")
       .ImplementedBy<CustomerImpl>()
       .Activator<MyCustomerActivator>()
   );

In this example we use generics to configure a component and bind implementation to specific type and use another concrete type to create that instance.

Another example which I use in my project is binding message types to handling method. We have a TCP client (non WCF – what a shame) that returns messages from a single event. Each message type should be handled by a different method (logic).

Using generics and method chaining got us the following solution:

var repo = new MessageHandlerContainer();
repo.Register<ConcreteMessage1>().With(SendEmailNotification)
    .Register<AnotherMessage>().With(SoundAlarm);

// when a message arrive
var newMessage = GetNextMessage();

container.ProcessMessage(newMessage);

Easy to understand what each the code does – without reading the actual implementation.

But just in case – here you go:

public class MessageHandlerContainer
{
    private readonly Dictionary<Type, IMessageHandler> _messageHandlers =
        new Dictionary<Type, IMessageHandler>();

    public void ProcessMessage(IMessage message)
    {
        _messageHandlers[message.GetType()].Run();
    }

    public MessageHandler<T> Register<T>() where T : IMessage
    {
        var messageHandler = new MessageHandler<T>();

        _messageHandlers.Add(typeof(T), messageHandler);

        return messageHandler;
    }
}

The trick in inside the MessageHandler class which adds an action and return the parent container so that you may register yet another message:

public class MessageHandler<T> : IMessageHandler
{
    private readonly MessageHandlerContainer _parent;
    private Action<IMessage> _actionToRun;

    public MessageHandler(MessageHandlerContainer parent)
    {
        _parent = parent;
    }

    public MessageHandlerContainer WithHandler(Action<IMessage> actionToRun)
    {
        _actionToRun = actionToRun;

        return _parent;
    }

    public void Run()
    {
        _actionToRun();
    }
}

It’s not the simplest code to follow – but when you see the usage example (scroll up) you’ll notice it makes a very readable API. Now it’s very simple to add another message handler to the logic without risking spaghetti code.

Conclusion

Generics are very powerful and can be used to in your fluent API to create and register specific types.

There are other uses for generics other than these two but you’ll have to wait for a different blog post – or experiment and find out by yourself.


Happy coding…

Labels: , ,