I'm constructing a fluent interface where I have a base class that contains the bulk of the fluent logic, and a derived class that add some specialized behavior. The problem I'm facing is the return type of the fluent methods in the base class when called from an instance of the derived type. After invoking a method of the base class, only methods of the base class remain available for further fluent invocations.
Changing the order in which the methods are invoked will help it to compile, but it makes it less readable which is kinda the point for fluent interfaces. Is there a way to define some sort of "This" type for the the base class so that all methods return the same type.
public class Field<T>
{
public Field<T> Name( string name )
{
_name = name;
return this;
}
}
public SpecialField<T> : Field<T>
{
public SpecialField<T> Special(){ return this; }
}
// !!! Arrgh. Special is not a member of the Field<T> class.
var specialField = new SpecialField()
.Name( "bing" )
.Special();
I've tried solving it by doing something like the following but it's not valid C# :( but at least expresses how I'd like to code the interface.
public class Field<T,TThis> : TThis
where TThis : Field<T,TThis>
{
public TThis Name( string name ){...}
}
public SpecialField<T> : Field<T,SpecialField<T>>
{
public TThis Special(){ return this; }
}
A fluent interface is an object-oriented API that depends largely on method chaining. The goal of a fluent interface is to reduce code complexity, make the code readable, and create a domain specific language (DSL). It is a type of method chaining in which the context is maintained using a chain.
Fluent is an open-source, cross-platform design system that gives designers and developers the frameworks they need to create engaging product experiences—accessibility, internationalization, and performance included.
In software engineering, a fluent interface is an object-oriented API whose design relies extensively on method chaining. Its goal is to increase code legibility by creating a domain-specific language (DSL). The term was coined in 2005 by Eric Evans and Martin Fowler.
Fluent.Net is a C# implementation of Project Fluent, a localization framework designed to unleash the expressive power of the natural language. Project Fluent keeps simple things simple and makes complex things possible. The syntax used for describing translations is easy to read and understand.
After poking around some other fluent APIs I found how to do it. It's not quite as clean, but it works well. Basically you introduce an intermediary base class for each derived type that you want to use and it passes the "TThis" type to the actual implementation.
public class FieldBase<T,TThis>
where TThis : FieldBase<T,TThis>
{
private string _name;
public TThis Name( string name )
{
_name = name;
return (TThis)this;
}
}
public class Field<T> : FieldBase<T,Field<T>>{}
public class SpecialFieldBase<T,TThis> : FieldBase<T,TThis>
where TThis : SpecialFieldBase<T,TThis>
{
public TThis Special(){ return (TThis)this; }
}
public class SpecialField<T> : SpecialFieldBase<T,SpecialField<T>>{}
// Yeah it works!
var specialField = new SpecialField<string>()
.Name( "bing" )
.Special();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With