Creating a fluent interface for your .Net class

Éric MoreauSenior .Net Consultant
CERTIFIED EXPERT
Senior Consultant for .Net VB & C# developer (mostly for Windows Forms project type). Also a nominated as a Microsoft MVP from 2004 to 2017.
Published:
Updated:
I have seen a presentation lately about unit testing. One of the aspect covered by the presentation was the use of fluent interface to ease the wording of the tests (and ease the reading at the same time).

Is that limited to unit testing? Of course not. What a great way to provide an API to many of our classes.

This article will show you how to create fluent interface in your own classes to use from anywhere you are using your classes.

What it is


Consider this first example:

Dim s As String = "Eric Moreau"
                      s = s.Trim()
                      s = s.ToUpper()
                      s = s.Replace("R", "z")

Open in new window


The readability of these statements is arguable at best and it is not very efficient (knowing that strings are immutable). It is also a bit too long to write. We don’t want to always break line and assign again.

Another way of writing the same as above, is this simple line:

Dim s As String = "Eric Moreau".Trim().ToUpper().Replace("R", "z")

Open in new window


This is an example of a fluent interface that you have surely used for years. The result of the first string is passed to the Trim method which is in turn passed to the ToUpper method to finally be passed to the Replace method before being returned to the variable.

If you have used LINQ, Entity Framework, NHibernate, most mocking tools, and many others, you have already used fluent interfaces maybe without explicitly knowing you were using them.

Fluent interface is normally implemented by using method cascading to relay the instruction context of a subsequent call (source: Wikipedia). A different way of explaining it is that it is a sequence of methods that can be chained one after the other.

Here is an example of a LINQ query that uses fluent interface (exposed by extension methods in this case):

Dim dir As New IO.DirectoryInfo("C:\Windows\System32")
                      Dim fileList = dir.GetFiles("*.*").
                          Where(Function(f) f.Name.StartsWith("odbc")).
                          OrderBy(Function(f) f.FullName)

Open in new window


The Where and the OrderBy methods chains on the result of the GetFiles method and it reads very fluently (even if I prefer the lambda format of C# over the one of VB but that’s another story).

Downloadable demo application


The downloadable demo solution of this month contains both VB and C#.

Figure 1: The demo application in action
The fluent interface demo application

Alternatives


Through the years, developers asked Microsoft to help them save a few keystrokes and make their code more readable. Here is a sample of what we are trying to improve here:

Dim emp As Employee = New Employee
                      emp.Name = "Eric Moreau"
                      emp.Dob = New DateTime(1970, 5, 28)
                      emp.HireDate = New DateTime(2007, 2, 1)
                      emp.Salary = 123456
                      emp.BossName = "Pointy-Haired Boss"

Open in new window


Two notable mentions (only 1 if you are doing C#) when comes to object declaration and initialization are still very valid.

Fluent interface is a lot more than just initialization as you have seen in the 2 examples above.

Alternative 1 – Object initializer


Starting with .Net Framework 3.5 (Visual Studio 2008), after many years of C# developers (with many coming from the VB world) complained about the missing With statement, Microsoft released the object initializer as you can see in this example:

Dim emp2 As Employee = New Employee With {
                          .Name = "Eric Moreau",
                          .Dob = New DateTime(1970, 5, 28),
                          .HireDate = New DateTime(2007, 2, 1),
                          .Salary = 123456,
                          .BossName = "Pointy-Haired Boss"
                      }

Open in new window


This syntax really helps shorten the initialization (and is of great help with collections) but won’t let you call methods!

Alternative 2 – With … End With (VB only)


This VB-only syntax (that C# developers complained about) has been existing for years. Even before .Net. I have done VB since its version 4 (yes, I am that old) and I remember it was there.

The idea is to surround the statements as shown here:

Dim emp3 As Employee = New Employee
                      With emp3
                          .Name = "Eric Moreau"
                          .Dob = New DateTime(1970, 5, 28)
                          .HireDate = New DateTime(2007, 2, 1)
                          .Salary = 123456
                          .BossName = "Pointy-Haired Boss"
                      End With

Open in new window


Notice that this syntax is not only for object initialization has it works with both properties and methods.

Back to Fluent interface


So how can we achieve something that would work with both properties and methods to get a fluent interface?

There won’t be a compiler trick or special keywords. You will need to write some methods in your classes to provide this feature. How much code? Well at least one method for everything you want to chain. It won’t be complex code. The syntax I prefer also requires an interface.

Basic class


Look at this very simplified class just for the demo purpose:

Public Class Employee
                      
                          Public Name As String
                          Public Dob As Date
                          Public HireDate As Date
                          Public Salary As Int32
                          Public BossName As String
                      
                          Public Overrides Function ToString() As String
                              Return String.Format(" The employee {0} was hired {1:D} with a salary of {2:c} reports to {3}. He was born on {4:D}", Name, HireDate, Salary, BossName, DOB)
                          End Function
                      
                      End Class

Open in new window


The ToString override is simply to ease the debugging.

Let’s say we want to “fluentize” it.

“Fluentize” the class


The way I prefer (because if you dig, you might find other) requires an interface like this one

Public Interface IEmployeeFluent
                      
                          Function OfName(ByVal pName As String) As IEmployeeFluent
                          Function AsOf(ByVal pDate As Date) As IEmployeeFluent
                          Function BornOn(ByVal pDate As Date) As IEmployeeFluent
                          Function SetSalary(ByVal pSalary As Int32) As IEmployeeFluent
                          Function ReportTo(ByVal pName As String) As IEmployeeFluent
                      
                      End Interface

Open in new window


In the previous section, my class was showing 5 properties. My interface here shows 5 methods, one for each property because I have decided so. It doesn’t have to be a 1-for-1 relation but it often makes sense.

Now that we have our interface, we need to implement it in the base class.

This is how I have implemented the interface for my class:

Public Function OfName(ByVal pName As String) As IEmployeeFluent Implements IEmployeeFluent.OfName
                      	Name = pName
                      	Return Me
                      End Function
                      
                      Public Function AsOf(ByVal pDate As Date) As IEmployeeFluent Implements IEmployeeFluent.AsOf
                      	HireDate = pDate
                      	Return Me
                      End Function
                      
                      Public Function BornOn(ByVal pDate As Date) As IEmployeeFluent Implements IEmployeeFluent.BornOn
                      	DOB = pDate
                      	Return Me
                      End Function
                      
                      Public Function SetSalary(ByVal pSalary As Integer) As IEmployeeFluent Implements IEmployeeFluent.SetSalary
                      	Salary = pSalary
                      	Return Me
                      End Function
                      
                      Public Function ReportTo(ByVal pName As String) As IEmployeeFluent Implements IEmployeeFluent.ReportTo
                      	BossName = pName
                      	Return Me
                      End Function

Open in new window


Simple no? The trick when we want to have a fluent interface is that each method will return the object on which it is working. It affects a property and returned the current instance. We can now use our new fluent object with something like this:

Dim emp2 As IEmployeeFluent = New Employee()
                      MessageBox.Show(emp2.
                          OfName("Eric Moreau").
                          AsOf(New DateTime(2007, 2, 1)).
                          BornOn(New DateTime(1970, 5, 28)).
                          SetSalary(123456).
                          ReportTo("Pointy-Haired Boss").
                          ToString())

Open in new window


But usually, when we have a fluent interface, we don’t have to create an instance. Something in the chain will create one for us if we need to. This is why I have created a Shared method (static in C#) to return an instance of a new employee:

Public Shared Function Hire() As IEmployeeFluent
                      	Return New Employee
                      End Function

Open in new window


Now that I have this Hire method, I don’t have to create an instance prior to call my chain of operations. Code will look like this:

Dim emp As IEmployeeFluent = Employee.Hire().
                          OfName("Eric Moreau").
                          AsOf(New DateTime(2007, 2, 1)).
                          BornOn(New DateTime(1970, 5, 28)).
                          SetSalary(123456).
                          ReportTo("Pointy-Haired Boss")

Open in new window


You can chain as many methods as you want as long as they are all returning an instance of your object.

The one exception is the last one. In a previous example, I have used MessageBox to show the employee data using the ToString method. This last method does not return an instance of an employee and we don’t really care as it is the last operation on the chain.

Debugging


Debugging can be harder as you now have a single line of code instead of multiple calls.

If you are the owner of the class, you can set a breakpoint in the fluent method and you will stop exactly where you want.

If you don’t have the code of that object, you will need to return to non-fluent method calls just for the sake of finding the issue.

Conclusion


As you have seen, adding a fluent interface to your existing classes is not a big deal but can makes your code a lot easier to read.

Fluent interface allows you to modify the API of your existing class with a more descriptive interface which improves its usability. At the same time, this preserves the current API interface and eliminates the risk of introducing bugs to code that is already implemented.
emoreau-DemoFluentInterface.zip
4
3,357 Views
Éric MoreauSenior .Net Consultant
CERTIFIED EXPERT
Senior Consultant for .Net VB & C# developer (mostly for Windows Forms project type). Also a nominated as a Microsoft MVP from 2004 to 2017.

Comments (1)

Éric MoreauSenior .Net Consultant
CERTIFIED EXPERT
Distinguished Expert 2022

Author

Commented:
Done.

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.