Method Naming
Fluent interfaces lend themselves to readability as long as the method names are chosen sensibly.
With that in mind, I'd like to nominate this particular API as "anti-fluent":
System.Type.IsInstanceOfType
It's a member of System.Type
and takes an object, and returns true if the object is an instance of the type. Unfortunately, you naturally tend to read it from left to right like this:
o.IsInstanceOfType(t); // wrong
When it's actually the other way:
t.IsInstanceOfType(o); // right, but counter-intuitive
But not all methods could possibly be named (or positioned in the BCL) to anticipate how they might appear in "pseudo-English" code, so this isn't really a criticism. I'm just pointing out another aspect of fluent interfaces - the choosing of method names in order to cause the least surprise.
Object Initializers
With many of the examples given here, the only reason a fluent interface is being used is so that several properties of a newly allocated object can be initialized within a single expression.
But C# has a language feature that very often makes this unnecessary - object initializer syntax:
var myObj = new MyClass
{
SomeProperty = 5,
Another = true,
Complain = str => MessageBox.Show(str),
};
This perhaps would explain why expert C# users are less familiar with the term "fluent interface" for chaining calls on the same object - it isn't needed quite so often in C#.
As properties can have hand-coded setters, this is an opportunity to call several methods on the newly constructed object, without having to make each method return the same object.
The limitations are:
- A property setter can only accept one argument
- A property setter cannot be generic
I would like it if we could call methods and enlist in events, as well as assign to properties, inside an object initializer block.
var myObj = new MyClass
{
SomeProperty = 5,
Another = true,
Complain = str => MessageBox.Show(str),
DoSomething()
Click += (se, ev) => MessageBox.Show("Clicked!"),
};
And why should such a block of modifications only be applicable immediately after construction? We could have:
myObj with
{
SomeProperty = 5,
Another = true,
Complain = str => MessageBox.Show(str),
DoSomething(),
Click += (se, ev) => MessageBox.Show("Clicked!"),
}
The with
would be a new keyword that operates on an object of some type and produces the same object and type - note that this would be an expression, not a statement. So it would exactly capture the idea of chaining in a "fluent interface".
So you could use initializer-style syntax regardless of whether you'd got the object from a new
expression or from an IOC or factory method, etc.
In fact you could use with
after a complete new
and it would be equivalent to the current style of object initializer:
var myObj = new MyClass() with
{
SomeProperty = 5,
Another = true,
Complain = str => MessageBox.Show(str),
DoSomething(),
Click += (se, ev) => MessageBox.Show("Clicked!"),
};
And as Charlie points out in the comments:
public static T With(this T with, Action<T> action)
{
if (with != null)
action(with);
return with;
}
The above wrapper simply forces a non-returning action to return something, and hey presto - anything can be "fluent" in that sense.
Equivalent of initializer, but with event enlisting:
var myObj = new MyClass().With(w =>
{
w.SomeProperty = 5;
w.Another = true;
w.Click += (se, ev) => MessageBox.Show("Clicked!");
};
And on a factory method instead of a new
:
var myObj = Factory.Alloc().With(w =>
{
w.SomeProperty = 5;
w.Another = true;
w.Click += (se, ev) => MessageBox.Show("Clicked!");
};
I couldn't resist giving it the "maybe monad"-style check for null as well, so if you have something that might return null
, you can still apply With
to it and then check it for null
-ness.