使用 Func 的责任链
Chain of responsibility using Func
我正在使用 System.Func<T, T>
创建责任链管道,其中管道中的每个函数都包含对下一个函数的引用。
构建管道时,我无法通过引用传递内部函数,因为它由于重新分配管道函数而抛出 WhosebugException,例如:
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"); // WhosebugException
我可以用闭包解决这个问题:
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");
但是,我想知道是否有更有效的方法来构建这个函数链,也许使用表达式?
使用表达式,由于编译表达式的成本,过程的 "building" 部分肯定效率较低,可能至少比链接 Func
慢两个数量级s.
深入表达式 - 管道的元素是表达式本身而不是 Func
s,可用于通过重写创建更多 runtime-efficient 的实现。设置速度较慢,但基本上每次您都会收到一个元素的表达式,例如:
Expression<Func<string, Func<string, string>, string>> handler1 = (s, next) =>
next.Invoke(s.ToUpper());
您重写它,以便 next
中应该包含的内容直接内联到 next.Invoke(...)
出现在表达式树中的位置。
虽然这确实将您限制为 expression-bodied 个元素(实际上,您只需要让任何复杂处理程序的主体调用辅助函数,而不是执行它们需要内联完成的任何工作)。
试图在某处挖掘出这样的例子,但想不出一个好的例子。祝你好运!
责任链在我看来就像一个链表。通常,它会创建一个 class 来封装每个包含对链中下一个处理程序的引用的处理程序。但是如果你想使用 Func
风格,我们可以使用程序风格做类似的事情:
此处演示: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;
};
我正在做的是创建一个高阶函数来构建链。
另一个使用相同想法链接 3 个处理程序的演示:https://dotnetfiddle.net/ni0DKL
不过,我建议为此创建一个 class:https://dotnetfiddle.net/CsVpzh。从封装和抽象的角度来看更好,并且易于扩展以向每个处理程序添加特定配置。
那个呢?这样你就可以构建任意长度的链。
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;
}
我正在使用 System.Func<T, T>
创建责任链管道,其中管道中的每个函数都包含对下一个函数的引用。
构建管道时,我无法通过引用传递内部函数,因为它由于重新分配管道函数而抛出 WhosebugException,例如:
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"); // WhosebugException
我可以用闭包解决这个问题:
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");
但是,我想知道是否有更有效的方法来构建这个函数链,也许使用表达式?
使用表达式,由于编译表达式的成本,过程的 "building" 部分肯定效率较低,可能至少比链接 Func
慢两个数量级s.
深入表达式 - 管道的元素是表达式本身而不是 Func
s,可用于通过重写创建更多 runtime-efficient 的实现。设置速度较慢,但基本上每次您都会收到一个元素的表达式,例如:
Expression<Func<string, Func<string, string>, string>> handler1 = (s, next) =>
next.Invoke(s.ToUpper());
您重写它,以便 next
中应该包含的内容直接内联到 next.Invoke(...)
出现在表达式树中的位置。
虽然这确实将您限制为 expression-bodied 个元素(实际上,您只需要让任何复杂处理程序的主体调用辅助函数,而不是执行它们需要内联完成的任何工作)。
试图在某处挖掘出这样的例子,但想不出一个好的例子。祝你好运!
责任链在我看来就像一个链表。通常,它会创建一个 class 来封装每个包含对链中下一个处理程序的引用的处理程序。但是如果你想使用 Func
风格,我们可以使用程序风格做类似的事情:
此处演示: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;
};
我正在做的是创建一个高阶函数来构建链。 另一个使用相同想法链接 3 个处理程序的演示:https://dotnetfiddle.net/ni0DKL
不过,我建议为此创建一个 class:https://dotnetfiddle.net/CsVpzh。从封装和抽象的角度来看更好,并且易于扩展以向每个处理程序添加特定配置。
那个呢?这样你就可以构建任意长度的链。
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;
}