Func Responsibility Chain

I create a chain of responsibility using System.Func<T, T> , where each function in the pipeline contains a link to the following.

When building a pipeline, I cannot pass an internal function by reference, because it throws a StackOverflowException due to a 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 get around this with closing:

 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 create this chain of functions, possibly using expressions?

+5
source share
3 answers

How about this? 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; } 
+1
source

Using expressions, the β€œbuilding” part of the process is guaranteed to be less efficient due to the cost of compiling expressions, probably at least two orders of magnitude slower than binding Func s.

Going deep into expressions where the pipeline elements are the expressions themselves and not Func s, can be used to create a more efficient implementation at runtime by rewriting. Slowly configure, but essentially every time an expression is passed to you for an element of type:

 Expression<Func<string, Func<string, string>, string>> handler1 = (s, next) => next.Invoke(s.ToUpper()); 

You rewrite it so that the body of what should be in next is simply embedded directly where next.Invoke(...) appears in the expression tree.

This limits you to elements with an expression (although you just need to get the body of any complex handlers to call a helper function instead of doing any work necessary to do the built-in).

Trying to snatch an example of this out there somewhere, but can't think of the good from my head. Good luck

+1
source

The chain of responsibility is like a linked list from my point of view. Usually, he created a class to encapsulate each handler, which contains a link to the next handler in the chain. But if you want to go with the Func style, we could do something similar using the 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 am doing is creating a higher order function for chaining. Another demo chain for three handlers using the same idea: https://dotnetfiddle.net/ni0DKL

However, I recommend creating a class for this: https://dotnetfiddle.net/CsVpzh . This is better in terms of encapsulation and abstraction and is easily extensible to add specific configurations for each handler.

+1
source

Source: https://habr.com/ru/post/1275929/


All Articles