Search code examples
c#inheritancefluent-interface

Fluent interfaces and inheritance in C#


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?


Solution

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