Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fluent interface configuration with lambdas in C#

Many open source projects use a Configuration class and lambda's to clarify configuring a complex object. Take Mass Transit for example. A simple configuration would be like so.

Bus.Initialize(sbc =>
        {
            sbc.UseMsmq();
            sbc.VerifyMsmqConfiguration();
            sbc.VerifyMsDtcConfiguration();
            sbc.UseMulticastSubscriptionClient();
            sbc.ReceiveFrom("msmq://localhost/test");
        });

When you hover over Initialize in Visual Studio it says the parameter for the method call is Action<ServiceBusConfigurator>. I was wondering if anyone could show a simple example of how to use this pattern on a class. I don't even know what to call this type of pattern and my "GoogleFu" is not working as of yet. In this particular case I realize the method is operating on a singleton pattern. But I am ok with it being an instance method on a class.

like image 363
Matt Phillips Avatar asked Jan 30 '12 23:01

Matt Phillips


2 Answers

An Action<ServiceBusConfigurator> is a method which accepts a single parameter of type ServiceBusConfigurator, does an "action" operating on that instance, and returns nothing (void).

.NET BCL (starting from 3.5) comes with predefined generic delegate signatures: Action<T>, Action<T1, T2> (etc.) for methods which don't return a value, and Func<Tresult>, Func<T, Tresult> (etc.) for methods accepting zero of more parameters and returning a single result instance of type Tresult.

When you create a method which accepts a delegate, you allow callers of your method to pass more than just data parameters - your method actually delegates a part of responsibility to external code. In your case, Bus.Initialize creates an instance of ServiceBusConfigurator (sbc), and then calls the specified action with the sbc instance as the parameter.

This basically lets your method control the lifetime of the configuration class instance. It is up to the caller to fill in the details, but actual instance is provided by your class:

 // this is not actual mass transit source code
 public class BusCreator
 {
     public static IBus Initialize(Action<IConfiguration> action)
     {
         // create the config instance here
         IConfiguration config = CreateDefaultConfig();

         // let callers modify it
         action(config);

         // use the final version to build the result
         return config.Build()
     }
 }

The benefit is that your built instance (the imaginary IBus in this case) cannot be modified further. Configuration instance is only created shortly, passed to an external method, and then used to create an immutable final object:

 IBus result = BusCreator.Configure(cfg => cfg.BusType = BusType.MSMQ);

Two things to note in the line above:

  1. The code inside the anonymous method is wrapped inside a delegate passed to the method. It is not executed until the Configure method actually calls it.

  2. The cfg parameter is created by the Configure method and passed to the lambda. After the method returns, this object doesn't exist anymore (or is wrapped inside the resulting object).

like image 168
Groo Avatar answered Nov 16 '22 10:11

Groo


To add to what others have said, this is an "entry point" into a fluent interface. The approach of using an Action callback to achieve this is a nice way of isolating the fluent interface in a way that is at the same time very extensible.

like image 34
Pete Avatar answered Nov 16 '22 11:11

Pete