Method chaining, the core concept behind building fluent interfaces is to allow for better readability. The key to method chaining is to have the extension return an instance of the caller. In later recipes, most extension methods you use will be chained extension methods. If you have used jQuery before, then you have experienced method chaining. This recipe should give you the basic understanding of how and when to chain a method.
Refer to the UserExtensions.cs
and Models/Users.cs
files in the ExtensionMethods.Library
project for the extension methods and class. These methods are used in the Program.cs
file in the ExtensionMethods.Console
project.
The following code is an upgrade of the previous SetName
method that allows chaining:
/// <summary> /// This is a chained version of SetNames /// </summary> /// <param name="value"></param> /// <param name="fullName"></param> public static User SetNamesChained(this User value, string fullName) { //the default delimiter is whitespace if no params are passed. string[] names = fullName.Split(); value.FirstName = names[0]; value.LastName = names[1]; return value; }
The following code shows the use of the extension methods:
User user = new User(); string fullName = user.SetNamesChained("Derron Brown").GetFullName();
Method chaining increases code readability, but makes debugging tricky to accomplish; you cannot put break points at each method call to know the value returned by a particular method or which method threw an exception without examining the stack trace. We can only chain methods that return its instance. As seen in the extension usage snippet, we can call another method directly after the SetNamesChained
method was called.
Method chaining is mainly used in fluent APIs, such as querying or in very verbose situations like validating and testing conditions. An example of testing conditions would be in the CuttinEdge.Conditions
library:
public void GetData(int? id) { Condition.Requires(id, "id") .IsNotNull() // throws ArgumentNullException on failure .IsInRange(1, 999) // ArgumentOutOfRangeException on failure .IsNotEqualTo(128);// throws ArgumentException on failure }
Now, the code is much more readable. In this recipe, we have learned one of the core features used by LINQ and other query mechanisms which we will use in later recipes.