I'm creating a chain of responsibility pipeline using System.Func<T, T>
where each function in the pipeline holds a reference to the next.
When building the pipeline, I'm unable to pass the inner function by reference as it throws a StackOverflowException due to the reassignment of the pipeline function, for example:
Func<string, Func<string, string>, string> handler1 = (s, next) => {
s = s.ToUpper();
return next.Invoke(s);
};
Func<string, string> pipeline = s => s;
pipeline = s => handler1.Invoke(s, pipeline);
pipeline.Invoke("hello"); // StackOverFlowException
I can work around this with a closure:
Func<string, Func<string, string>, string> handler1 = (s, next) => {
s = s.ToUpper();
return next.Invoke(s);
};
Func<Func<string, string>, Func<string, string>> closure =
next => s => handler1.Invoke(s, next);
Func<string, string> pipeline = s => s;
pipeline = closure.Invoke(pipeline);
pipeline.Invoke("hello");
However, I would like to know if there is a more efficient way to build up this chain of functions, perhaps using Expressions?
What about that? This way you can build chains of arbitrary length.
void Main()
{
Func<string, string> f1 = x => x.Replace("*", string.Empty);
Func<string, string> f2 = x => x.Replace("--", string.Empty);
Func<string, string> f3 = x => x.ToUpper();
//Func<string, string> pipeline = x => f3(f2(f1(x)));
Func<string, string> pipeline = Pipeline(f1, f2, f3);
pipeline.Invoke("te-*-st").Dump(); // prints "TEST"
}
Func<T, T> Pipeline<T>(params Func<T, T>[] functions)
{
Func<T, T> resultFn = x => x;
for (int i = 0; i < functions.Length; i++)
{
Func<T, T> f = functions[i];
Func<T, T> fPrev = resultFn;
resultFn = x => f(fPrev(x));
}
return resultFn;
}
Using expressions, the "building" part of the process is guaranteed to be less efficient because of the cost of compiling the expressions, probably at least two orders of magnitude slower than linking up the Func
s.
Going deep with expressions - where the elements of the pipeline are expressions themselves rather than Func
s, can be used to create a more runtime-efficient implementation by rewriting. Slower to set up, but essentially every time you are handed an expression for an element like:
Expression<Func<string, Func<string, string>, string>> handler1 = (s, next) =>
next.Invoke(s.ToUpper());
You rewrite it so that the body of whatever is supposed to be in next
just gets inlined directly in where next.Invoke(...)
appears in the expression tree.
This does limit you to expression-bodied elements though (effectively, you just need to make the body of any complex handlers call a helper function instead of doing whatever work they need to do inline).
Trying to dredge up an example of this out there somewhere, but can't think of a good one off the top of my head. Good luck!
Chain of responsibility is like a linked list from my point of view. Normally, it would create a class to encapsulate each handler that contains a reference to the next handler in the chain. But if you want to go with Func
style, we could do something similar using procedural style:
Demo here: https://dotnetfiddle.net/LrlaRm
public static void Main()
{
Func<string, string> handler1 = (s) => {
s = s.ToUpper();
return s;
};
Func<string, string> handler2 = (s) => {
s = s.TrimStart();
return s;
};
Func<string, string> chain = ChainBuilder(handler1, handler2);
Console.WriteLine(chain(" hello"));
}
static Func<Func<string, string>, Func<string, string>, Func<string, string>> ChainBuilder = (f1, f2) => s => {
s = f1(s);
if (f2 != null) {
s = f2(s);
}
return s;
};
What i'm doing is creating a higher order function to build the chain. Another demo to chain 3 handlers using the same idea: https://dotnetfiddle.net/ni0DKL
However, I recommend creating a class for this: https://dotnetfiddle.net/CsVpzh. It's better from encapsulation & abstraction points of view and easy to extend to add specific configurations to each handler.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With