Method-Chaining in C#

Method-Chaining in C#

The technique you mention is called chainable methods. It is commonly used when creating DSLs or fluent interfaces in C#.

The typical pattern is to have your AddItem() method return an instance of the class (or interface) it is part of. This allows subsequent calls to be chained to it.

public MyCollection AddItem( MyItem item )
{
// internal logic...

return this;
}

Some alternatives to method chaining, for adding items to a collection, include:

Using the params syntax to allow multiple items to be passed to your method as an array. Useful when you want to hide the array creation and provide a variable argument syntax to your methods:

public void AddItems( params MyItem[] items )
{
foreach( var item in items )
m_innerCollection.Add( item );
}

// can be called with any number of arguments...
coll.AddItems( first, second, third );
coll.AddItems( first, second, third, fourth, fifth );

Providing an overload of type IEnumerable or IEnumerable so that multiple items can be passed together to your collection class.

public void AddItems( IEnumerable<MyClass> items )
{
foreach( var item in items )
m_innerCollection.Add( item );
}

Use .NET 3.5 collection initializer syntax. You class must provide a single parameter Add( item ) method, implement IEnumerable, and must have a default constructor (or you must call a specific constructor in the initialization statement). Then you can write:

var myColl = new MyCollection { first, second, third, ... };

Method chaining in C#

You could write a C# extensions,

e.g.

GetAddResult(num1, num2).Handle();

where the extension would be:

public static void Handle(this AddResult addResult)
{
var transformedResult = TransformResult(addResult);

if(CheckValidity(transformedResult))
{
SendResult(transformedResult);
}
else
{
LogError(transformedResult);
}
}

OR a bit more verbose:

GetAddResult(num1, num2).ToTransformResult().HandleValidity();

with these extensions:

public static TransformResult ToTransformResult(this AddResult addResult)
{
return TransformResult(addResult);
}

public static void HandleValidity(this TransformResult addResult)
{
if(CheckValidity(transformedResult))
{
SendResult(transformedResult);
}
else
{
LogError(transformedResult);
}
}

Not sure if that would fullfil all your requirements.

chaining methods in base and derived class

Use generic extension methods

Fluent/chaining methods work best as generic extension methods. A generic extension method knows the type of the instance variable and can return it as the same type that was passed in.

class Animal
{
public string CommonProperty { get; set; }
}

class Dog : Animal
{
public string DogOnlyProperty { get; set; }
}

static class ExtensionMethods
{
static public T AnimalMethod<T>(this T o) where T : Animal
{
o.CommonProperty = "foo";
return o;
}
static public T DogMethod<T>(this T o) where T : Dog
{
o.DogOnlyProperty = "bar";
return o;
}

}

class Example
{
static public void Test()
{
var dog = new Dog();
dog.DogMethod().AnimalMethod(); // 1 - this works
dog.AnimalMethod().DogMethod(); // 2 - this works now

Console.WriteLine("CommonProperty = {0}", dog.CommonProperty);
Console.WriteLine("DogOnlyProperty = {0}", dog.DogOnlyProperty);

var animal = new Animal();
animal.AnimalMethod();
//animal.DogMethod(); //Does not compile
//animal.AnimalMethod().DogMethod(); //Does not compile
}
}

Output:

CommonProperty = foo

DogOnlyProperty = bar

A workaround if you need private/protected access

One disadvantage of extension methods is that they cannot access private or protected members. Your instance method could. This hasn't been a problem for me (and it seems it's not an issue for the entire LINQ library either, which are written as extension methods). But there is a workaround if you need access.

You will need to implement the "chaining" method twice-- once as an interface method on the instance and a simple wrapper (one line of code) as an extension method that simply calls the first method. We use an interface method on the instance so that the compiler won't try to pick the instance method over the extension method.

interface IPrivateAnimal
{
Animal AnimalMethod();
}

interface IPrivateDog
{
Dog DogMethod();
}

class Animal : IPrivateAnimal
{
protected virtual string CommonProperty { get; set; } //notice this is nonpublic now

Animal IPrivateAnimal.AnimalMethod() //Won't show up in intellisense, as intended
{
this.CommonProperty = "plugh";
return this;
}
}

class Dog : Animal, IPrivateDog
{
private string DogOnlyProperty { get; set; } //notice this is nonpublic now

Dog IPrivateDog.DogMethod() //Won't show up in intellisense
{
this.DogOnlyProperty = "xyzzy";
return this;
}
}

static class ExtensionMethods
{
static public T AnimalMethod<T>(this T o) where T : class, IPrivateAnimal
{
return o.AnimalMethod() as T; //Just pass control to our hidden instance method
}
static public T DogMethod<T>(this T o) where T : class, IPrivateDog
{
return o.DogMethod() as T; //Just pass control to the instance method
}
}

class Example
{
static public void Test()
{
var dog = new Dog();
dog.DogMethod().AnimalMethod();
dog.AnimalMethod().DogMethod();

Console.WriteLine("CommonProperty = {0}", typeof(Dog).GetProperty("CommonProperty", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(dog));
Console.WriteLine("DogOnlyProperty = {0}", typeof(Dog).GetProperty("DogOnlyProperty", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(dog));
}
}

Output:

CommonProperty = plugh

DogOnlyProperty = xyzzy

C# method-chaining in generic classes with inheritance

Your WithYchangeByX method doesn't actually care about the generic parameter T that much, so you can introduce a non-generic interface IA, and make it work with that instead:

public interface IA
{
string X { get; }
string Y { get; }
IA WithX(string x);
IA WithY(string y);
}

public abstract class A<T> : IA where T : A<T>
{
public string X { get; }
public string Y { get; }

abstract public T WithX(string x);

abstract public T WithY(string y);

IA IA.WithX(string x) => WithX(x);

IA IA.WithY(string y) => WithY(y);
}

Then you can do:

public static List<IA> WithYchangeByX(this List<IA> list, string firstX, string newY)
{
for (int i = 0; i < list.Count; i++)
{
if (list[i].X.Split(' ')[0].Equals(firstX))
{
list[i] = list[i].WithY(newY);
}
}
return list;
}

You can then create a List<IA> and pass it to this method.

Note that now you lose the generic version of WithYchangeByXImpl. If you want to keep it, you can do something like:

private static List<T> WithYchangeByXImpl<T>(List<T> list, string firstX, string newY, Action<int, List<T>> consumer) where T: IA
{
for (int i = 0; i < list.Count; i++)
{
if (list[i].X.Split(' ')[0].Equals(firstX))
{
consumer(i, list);
}
}
return list;
}

// these look to be the same, but they actually infer a different type parameter for WithYchangeByXImpl
public static List<IA> WithYchangeByX(this List<IA> list, string firstX, string newY)
=> WithYchangeByXImpl(list, firstX, newY, (i, list) => list[i] = list[i].WithY(newY));

public static List<T> WithYchangeByX<T>(this List<T> list, string firstX, string newY) where T: A<T>
=> WithYchangeByXImpl(list, firstX, newY, (i, list) => list[i] = list[i].WithY(newY));

Now this code compiles:

var b1 = B.Default.WithX("string").WithY("string").WithBspecial(new DateTime(2000, 1, 28));
var b2 = B.Default.WithX("string").WithY("string").WithBspecial(new DateTime(2000, 1, 17));
var c1 = C.Default.WithX("string").WithY("string").WithCspecial(5);

List<IA> a1 = new List<IA> { b1, b2, c1 };

var a2 = a1.WithYchangeByX("string", "string");

How to implement method chaining?

Chaining is a good solution to produce new instance from existing instances:

public class MyInt
{
private readonly int value;

public MyInt(int value) {
this.value = value;
}
public MyInt Add(int x) {
return new MyInt(this.value + x);
}
public MyInt Subtract(int x) {
return new MyInt(this.value - x);
}
}

Usage:

MyInt x = new MyInt(10).Add(5).Subtract(7);

You can also use this pattern to modify an existing instance, but this is generally not recommended:

public class MyInt
{
private int value;

public MyInt(int value) {
this.value = value;
}
public MyInt Add(int x) {
this.value += x;
return this;
}
public MyInt Subtract(int x) {
this.value -= x;
return this;
}
}

Usage:

MyInt x = new MyInt(10).Add(5).Subtract(7);

Method chaining on list with condition

While you can indeed create an extension as suggested by others, I wouldn't do that in actual production code because it's defeats the purpose of Linq.

Linq is functional and good for processing sequences or streams. Each Linq chaining operator processes incoming data and transforms it to another data. Given extension you are looking for is procedural and doesn't do anything with the sequence.

Given also doesn't support lazy evaluation which is one of the features of Linq.

By introducing such extension you just making the code harder to read for the next person working on this code.

By contrast, good old if can be easily understood by everyone.

If you want to save couple of lines you can use ternary operator:

var someList = otherList.ReplaceAll(foo, bar);
someList = someCondition ? someList.ReplaceAll(someOtherFoo, someOtherBar) : someList;

C# Creating a Fluent API for chaining methods

You could implement something like this:

public class Fluent<TIn, TOut>
{
private readonly TIn _value;
private readonly Func<TIn, TOut> _func;

public Fluent(TIn value, Func<TIn, TOut> func)
{
_value = value;
_func = func;
}

public Fluent<TIn, TNewOut> Then<TNewOut>(Func<TOut, TNewOut> func)
=> new Fluent<TIn, TNewOut>(_value, x => func(_func(x)));

private TOut Calc() => _func(_value);

public static implicit operator TOut(Fluent<TIn, TOut> self) => self.Calc();
}

then you could chain multiple methods one after another and return what ever you want:

double f = new Fluent<int, int>(2, x => 2 * x)
.Then(x => 4 * x)
.Then(x => x / 0.5);
Tuple<double, double> t = new Fluent<int, int>(2, x => 2 * x)
.Then(x => new Tuple<double, double>(x,x));

n.b. You could also remove the overloaded implicit cast operator and make Calc method public. In that case, you could use var because there would be no ambiguity between Fluent<TIn, TOut> and TOut.

Best practice for method chaining (return this)

Personally, I think it's bad practice to chain method calls that have actual sideffects on the object instance. To be honest, I think both examples are quite ugly "hacks" whose only purpose is saving two lines of code. I don't think the result is actually more readable either.

If you want a record to be loaded immediately, I'd probably rather supply a constructor variant that takes the ID you load from and make the object auto-populate itself on construction, though when I think about it, I wouldn't bother at all to be honest - cramming more information in a single line does not make for more read- and maintainable code.



Related Topics



Leave a reply



Submit