Fluent interfaces and inheritance in C#

C#InheritanceFluent Interface

C# Problem Overview


I'll show a problem by example. There is a base class with fluent interface:

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);
	}
}

and a child class:

class FluentCustomer : FluentPerson
{
	private long _Id;

	private string _AccountNumber = String.Empty;

	public FluentCustomer WithAccountNumber(string accountNumber)
	{
		_AccountNumber = accountNumber;
		return this;
	}
	public FluentCustomer WithId(long id)
	{
		_Id = id;
		return this;
	}

	public override string ToString()
	{
		return base.ToString() + String.Format(" account number: {0} id: {1}", _AccountNumber, _Id);
	}
}

The problem is that when you call customer.WithAccountNumber("000").WithFirstName("John").WithLastName("Smith") you can't add .WithId(123) in the end because return type of the WithLastName() method is FluentPerson (not FluentCustomer).

How this problem usually solved?

C# Solutions


Solution 1 - C#

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);

Solution 2 - C#

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();

Solution 3 - C#

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);

Solution 4 - C#

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 where TC : GridEx > { > public TC Build(T type) > { > return (TC) this; > } > } > > public class GridExEx : GridEx > { >
> } > > class Program > { > static void Main(string[] args) > { > new GridExEx().Build(1); > } > }

Solution 5 - C#

 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();

Solution 6 - C#

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.

Solution 7 - C#

I know this is now an old question, but I wanted to share my thoughts about this with you.

What about separating fluency, which is a kind of mechanism, and your classes, when you can ? This would leave your classes pure.

What about something like this ?

The classes

    public class Person
    {
        public string FirstName { get; set; }
        public string LastName {get; set;}

        public override string ToString()
        {
            return $"First name: {FirstName} last name: {LastName}";
        }
    }

    public class Customer : Person
    {
        public string AccountNumber { get; set; }
        public long Id { get; set; }

        public override string ToString()
        {
            return base.ToString() + $" account number: {AccountNumber} id: {Id}");
        }
    }

A class that adds some fluent mechanism

    public class FluentCustomer 
    {
        private Customer Customer { get; }

        public FluentCustomer() : this(new Customer())
        {
        }

        private FluentCustomer(Customer customer)
        {
            Customer = customer;
        }

        public FluentCustomer WithAccountNumber(string accountNumber)
        {
            Customer.AccountNumber = accountNumber;
            return this;
        }

        public FluentCustomer WithId(long id)
        {
            Customer.Id = id;
            return this;
        }

        public FluentCustomer WithFirstName(string firstName)
        {
            Customer.FirstName = firstName;
            return this;
        }

        public FluentCustomer WithLastName(string lastName)
        {
            Customer.LastName = lastName;
            return this;
        }

        public static implicit operator Customer(FluentCustomer fc)
        {
            return fc.Customer;
        }

        public static implicit operator FluentCustomer(Customer customer)
        {
            return new FluentCustomer(customer);
        }
    }

An extension method to switch to fluent mode

    public static class CustomerExtensions 
    {

        public static FluentCustomer Fluent(this Customer customer)
        {
            return customer;
        }
    }

The same example as in question


        Customer customer = new Customer().Fluent()
                            .WithAccountNumber("000")
                            .WithFirstName("John")
                            .WithLastName("Smith")
                            .WithId(123);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionbniwredycView Question on Stackoverflow
Solution 1 - C#SteckView Answer on Stackoverflow
Solution 2 - C#Yann TrevinView Answer on Stackoverflow
Solution 3 - C#Dzmitry HubaView Answer on Stackoverflow
Solution 4 - C#baHIView Answer on Stackoverflow
Solution 5 - C#RameshVelView Answer on Stackoverflow
Solution 6 - C#richardtallentView Answer on Stackoverflow
Solution 7 - C#SkAlView Answer on Stackoverflow