I\'ll show a problem by example. There is a base class with fluent interface:
class FluentPerson
{
private string _FirstName = String.Empty;
private
Try to use some Extension methods.
static class FluentManager
{
public static T WithFirstName<T>(this T person, string firstName) where T : FluentPerson
{
person.FirstName = firstName;
return person;
}
public static T WithId<T>(this T customer, long id) where T : FluentCustomer
{
customer.ID = id;
return customer;
}
}
class FluentPerson
{
public string FirstName { private get; set; }
public string LastName { private get; set; }
public override string ToString()
{
return string.Format("First name: {0} last name: {1}", FirstName, LastName);
}
}
class FluentCustomer : FluentPerson
{
public long ID { private get; set; }
public long AccountNumber { private get; set; }
public override string ToString()
{
return base.ToString() + string.Format(" account number: {0} id: {1}", AccountNumber, ID);
}
}
after you can use like
new FluentCustomer().WithId(22).WithFirstName("dfd").WithId(32);
Logically you need to configure stuff from most specific (customer) to least specific (person) or otherwise it is even hard to read it despite the fluent interface. Following this rule in most cases you won't need get into trouble. If however for any reason you still need to mix it you can use intermediate emphasizing statements like
static class Customers
{
public static Customer AsCustomer(this Person person)
{
return (Customer)person;
}
}
customer.WIthLastName("Bob").AsCustomer().WithId(10);
public class FluentPerson
{
private string _FirstName = String.Empty;
private string _LastName = String.Empty;
public FluentPerson WithFirstName(string firstName)
{
_FirstName = firstName;
return this;
}
public FluentPerson WithLastName(string lastName)
{
_LastName = lastName;
return this;
}
public override string ToString()
{
return String.Format("First name: {0} last name: {1}", _FirstName, _LastName);
}
}
public class FluentCustomer
{
private string _AccountNumber = String.Empty;
private string _id = String.Empty;
FluentPerson objPers=new FluentPerson();
public FluentCustomer WithAccountNumber(string accountNumber)
{
_AccountNumber = accountNumber;
return this;
}
public FluentCustomer WithId(string id)
{
_id = id;
return this;
}
public FluentCustomer WithFirstName(string firstName)
{
objPers.WithFirstName(firstName);
return this;
}
public FluentCustomer WithLastName(string lastName)
{
objPers.WithLastName(lastName);
return this;
}
public override string ToString()
{
return objPers.ToString() + String.Format(" account number: {0}", _AccountNumber);
}
}
And invoke it using
var ss = new FluentCustomer().WithAccountNumber("111").WithFirstName("ram").WithLastName("v").WithId("444").ToString();
A solution where you need fluent interface, inheritance and also some generics...
Anyhow as I stated before: this is the only option if you want to use inheritance and access also protected members...
public class GridEx<TC, T> where TC : GridEx<TC, T> { public TC Build(T type) { return (TC) this; } } public class GridExEx : GridEx<GridExEx, int> { } class Program { static void Main(string[] args) { new GridExEx().Build(1); } }
You can use generics to achieve that.
public class FluentPerson<T>
where T : FluentPerson<T>
{
public T WithFirstName(string firstName)
{
// ...
return (T)this;
}
public T WithLastName(string lastName)
{
// ...
return (T)this;
}
}
public class FluentCustomer : FluentPerson<FluentCustomer>
{
public FluentCustomer WithAccountNumber(string accountNumber)
{
// ...
return this;
}
}
And now:
var customer = new FluentCustomer()
.WithAccountNumber("123")
.WithFirstName("Abc")
.WithLastName("Def")
.ToString();
Is a fluent interface really the best call here, or would an initializer be better?
var p = new Person{
LastName = "Smith",
FirstName = "John"
};
var c = new Customer{
LastName = "Smith",
FirstName = "John",
AccountNumber = "000",
ID = "123"
};
Unlike a fluent interface, this works fine without inherited methods giving back the base class and messing up the chain. When you inherit a property, the caller really shouldn't care whether FirstName
was first implemented in Person or Customer or Object.
I find this more readable as well, whether on one line or multiple, and you don't have to go through the trouble of providing fluent self-decorating functions that correspond with each property.