Understanding LINQ to Objects (2) Method Chaining

[LINQ via C# series

It is obvious the Where(), OrderBy(), Select() can be invoked fluently:

int[] source = new int[] { 0, 1, -2, 3, 24, 6, 3 };
var results = source.Where(item => item > 0 && item < 10)
                    .OrderBy(item => item)
                    .Select(item => item.ToString(CultureInfo.InvariantCulture));

This is called method chaining: after invoking one method, another method can be invoked immediately on the return value. The following code cause the same results:

var results = source.Where(item => item > 0)
                    .Where(item => item < 10)
                    .Select(item => item.ToString(CultureInfo.InvariantCulture))
                    .OrderBy(item => item);

Method chaining is implemented by fluent interface.

Normal interface design

To implement a class with several behaviors, this normal design can be figured out in one second:

public interface IOtaku
{
    string Name
    {
        get;
    }

    // Behaviors with no return value really needed.
    void Eat(string food);
    void Program(string language);
    void Sleep();
}

public class Person : IOtaku
{
    public Person(string name)
    {
        this.Name = name;
    }

    public string Name
    {
        get;
        private set;
    }

    public void Eat(string food)
    {
        Console.WriteLine("{0} eats {1}.", this.Name, food);
    }

    public void Program(string language)
    {
        Console.WriteLine("{0} programs {1}.", this.Name, language);
    }

    public void Sleep()
    {
        Console.WriteLine("{0} sleeps.", this.Name);
    }
}

The usage is also normal:

IOtaku dixin = new Person("Dixin");

// Behaves some actions in a day.
dixin.Eat("bread");
dixin.Program("C#");
dixin.Eat("rice");
dixin.Program("JavaScript");
dixin.Eat("noodles");
dixin.Program("F#");
dixin.Eat("watermelon");
dixin.Sleep();

There is nothing special.

Fluent interface design

If making the IOtaku interface more fluent, the method invocations can be a chain. The trick is to return the IOtaku by each method:

public interface IOtaku
{
    string Name
    {
        get;
    }

    // The only difference is to return IOtaku for method chaining.
    IOtaku Eat(string food);
    IOtaku Program(string language);
    IOtaku Sleep();
}

public class Person : IOtaku
{
    public Person(string name)
    {
        this.Name = name;
    }

    public string Name
    {
        get;
        private set;
    }

    public IOtaku Eat(string food)
    {
        Console.WriteLine("{0} eats {1}.", this.Name, food);
        // Returns IOtaku after completing the actions.
        return this;
    }

    public IOtaku Program(string language)
    {
        Console.WriteLine("{0} programs {1}.", this.Name, language);
        return this;
    }

    public IOtaku Sleep()
    {
        Console.WriteLine("{0} sleeps.", this.Name);
        return this;
    }
}

Here is the usage:

IOtaku dixin = new Person("Dixin");

// Each invocation returns an IOtaku, so another invocation can be made immediately.
dixin.Eat("bread").Program("C#").Eat("rice").Program("JavaScript").Eat("noodles")
     .Program("F#").Eat("watermelon").Sleep();

The fluent IOtaku interface makes the programming more fluent. The fluent interface is first introduced by Eric Evans and Martin Fowler.

Fluent extension methods

If there is no such Eat(), Program() behaviors included the original design of IOtaku interface and Person class:

public interface IOtaku
{
    // Complex behaviors are not contained.
    string Name
    {
        get;
    }
}

public class Person : IOtaku
{
    public Person(string name)
    {
        this.Name = name;
    }

    public string Name
    {
        get;
        private set;
    }
}

In the design, only basic information Name is included. Those Eat(), Program() behaviors need to be added to the IOtaku interface and Person class.

C# 3.0 provides extension method syntactic sugar, which can be used to extend classes, delegates, as well as interfaces. So the only things need to be is:

  • Define some Eat(), Program() extension method for IOtaku interface, which looks those methods are added to the IOtaku interface;
  • Return IOtaku itself from those methods, which makes these methods fluent.
public static class OtakuExtensions
{
    // Extends a behavior on IOtaku.
    public static IOtaku Eat(this IOtaku otaku, string food)
    {
        Console.WriteLine("{0} eats {1}.", otaku.Name, food);
        // Returns IOtaku to be fluent.
        return otaku;
    }

    public static IOtaku Program(this IOtaku otaku, string language)
    {
        Console.WriteLine("{0} programs {1}.", otaku.Name, language);
        return otaku;
    }

    public static IOtaku Sleep(this IOtaku otaku)
    {
        Console.WriteLine("{0} sleeps.", otaku.Name);
        return otaku;
    }
}

The usage remains the same:

IOtaku dixin = new Person("Dixin");

// Eat() is added to IOtaku.
// IOtaku has Eat(), and Person "is" IOtaku, so Person has Eat().
dixin.Eat("bread").Program("C#").Eat("rice");

Of course, those Eat(), Program() are not really defined on the IOtaku interface. According to this post the above sugar code will be compiled into normal static method invocations:

OtakuExtensions.Eat(
    OtakuExtensions.Program(
        OtakuExtensions.Eat(dixin, "bread"),
        "C#"), 
    "rice");

IEnumerable<T> Extensions

After understanding the fluent interface design and usage, now take a look at the IEnumerable<T> interface. A object whose type implements IEnumerable<T> (which implements IEnumerable) can be iterated in a foreach loop, then each data items can be pulled out:

namespace System.Collections.Generic
{
    public interface IEnumerable<T> : IEnumerable
    {
        IEnumerator<T> GetEnumerator();
    }
}

or in .NET 4.0:

namespace System.Collections.Generic
{
    public interface IEnumerable<out T> : IEnumerable
    {
        IEnumerator<T> GetEnumerator();
    }
}

The meaning of the out keyword is explained in my post Understanding C# Covariance And Contravariance (2) Interfaces.

Just like the third IOtaku interface above, IEnumerable<T> only contains basic information supporting foreach iteration. There is no methods like Where(), Select(), OrderBy(), etc. In .NET 3.5+ FCL, those methods are added to IEnumerable<T> by defining extension methods:

namespace System.Linq
{
    public static class Enumerable
    {
        public static IEnumerable<TSource> Where<TSource>(
            this IEnumerable<TSource> source, Func<TSource, bool> predicate)
        {
            // ...
        }

        public static IEnumerable<TResult> Select<TSource, TResult>(
            this IEnumerable<TSource> source, Func<TSource, TResult> selector)
        {
            // ...
        }

        // IOrderedEnumerable<T> "is" IEnumerable<T>, so OrderBy() is still fluent.
        public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(
            this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
        { 
            // ...
        }

        public static IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey>(
            this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
        { 
            // ...
        }
    }
}

It is obvious that

  • Where(), Select(), Orderby(), … methods are added to IEnumerable<T>;
  • Those methods are fluent by returning IEnumerable<T>.

Please notice OrderBy() and OrderByDescending() return IOrderedEnumerable<T> instead of IEnumerable<T>:

namespace System.Linq
{
    // IOrderedEnumerable<T> "is" IEnumerable<T>.
    public interface IOrderedEnumerable<TElement> : IEnumerable<TElement>, IEnumerable
    {
        IOrderedEnumerable<TElement> CreateOrderedEnumerable<TKey>(
            Func<TElement, TKey> keySelector, IComparer<TKey> comparer, bool descending);
    }
}
Since IOrderedEnumerable<T> "is" IEnumerable<T>, OrderBy() and OrderByDescending() are as fluent as Where() and Select().

Most of (if not all) pull-based .NET collections implements IEnumerable<T>, explicitly or implicitly. They can be roughly categorized like this:

image

Please notice Array is a little special:

namespace System
{
    [Serializable]
    [ComVisible(true)]
    public abstract class Array : ICloneable, IList, ICollection, IEnumerable, 
                                  IStructuralComparable, IStructuralEquatable
    {
        // ...
    }
}

It doesn’t implement IEnumerable<T> but IEnumerable. For a concrete array T[], CLR automatically make it inherit Array and implement IEnumerable<T>, ICollection<T>, and IList<T>, as long as T[] is:

  • single dimensional;
  • zero–lower bound.

Therefore, those fluent methods are added to most of (if not all) pull-based collections types, including arrays. And method chaining can be used on those types. Now take a look at the sample at the beginning of this post again:

// int[] "is" (implicitly) IEnumerable<int>.
int[] source = new int[] { 0, 1, -2, 3, 24, 6, 3 };
// So method chaining can happen.
IEnumerable<int> results = source.Where(item => item > 0 && item < 10)
                    .OrderBy(item => item)
                    .Select(item => item.ToString(CultureInfo.InvariantCulture));

It should be very clear why method chaining is available.

Method chaining in the real world

Fluent interface is the typical way to implement method chaining. The key point is: when Method() is invoked on T object, it return a T object [T T.Method()]. In the real world, method chainings are everywhere.

Method chaining can be implemented without interface. Take a look at the string:

string source = "...";
string result = source.Trim().Replace('a', 'b')
                      .Substring(1).Remove(2).ToUpperInvariant();

Another example is jQuery:

$("div.test").add("p.quote").addClass("blue").slideDown("slow");

 

Many years ago before there is jQuery, I also developed a JavaScript library called EasyJS, containing a selector with method chaining available.

Published Monday, March 08, 2010 1:09 AM by Dixin

Comments

# data extend | DATA

Sunday, March 07, 2010 3:12 PM by data extend | DATA

Pingback from  data extend | DATA

# re: Understanding LINQ to Objects (2) Method Chaining

Sunday, March 07, 2010 9:03 PM by mazhou

Good topic! but the problem is how we choose normal interface (without chaining) or fluent in different scenarios?

# re: Understanding LINQ to Objects (2) Method Chaining

Monday, March 08, 2010 9:20 AM by Dixin

Take a look at the first IOtaku design (normal) and the second one (fluent). In the functionality's perspective, there is no defference at all. The only deffence is the coding experience.

# re: Understanding LINQ to Objects (2) Method Chaining

Friday, March 12, 2010 3:13 AM by Dixin

But in functional programming's perspective, this kind of method chaining has great mathematical meaning. like SelectMany() is actually a >>= in Haskell. You can check out Monad in Wikipedia:

http://en.wikipedia.org/wiki/Monad_(functional_programming)

Be careful that Monad is crazy:)

# Soft | Pearltrees

Tuesday, January 24, 2012 6:13 PM by Soft | Pearltrees

Pingback from  Soft | Pearltrees

# re: Understanding LINQ to Objects (2) Method Chaining

Friday, February 10, 2012 5:27 PM by Yelinna

This is my Chain of Methods:

if (txtID.Text.Trim().Length == 0 || txtNombre.Text.Trim().Length == 0)

        MessageBox.Show("You Left One of Them Empty!!");

# re: Understanding LINQ to Objects (2) Method Chaining

Friday, February 10, 2012 5:31 PM by Dixin

Cool:)

Leave a Comment

(required) 
(required) 
(optional)
(required)