调用和聚合多播
一种聚合多播函数返回值的一般方法。
引言
以下文章展示了在 C# 中聚合多播器返回值的一种可能性。
由于事件、委托、多播以及所有相关内容都已讨论过很多次,我将完全忽略这些主题,并假设读者熟悉它们。
背景
我经常需要安全地(且同步地)调用(可能未分配的)事件处理程序并聚合返回值。
一个非常常见的模式是 before- 和 afterEdit 模式,监听器可以在其中取消编辑。例如,MS 在其某些基于 EventArgs
的事件参数类中提供了 Cancel
属性,例如 WinForms TabControl
的 "Selecting" 事件具有 TabControlCancelEventArgs
,其中包含一个布尔类型的 Cancel
属性。
因为可能有很多监听器可能会将 Cancel
属性设置为 true
,所以调用者必须检查是否至少有一个监听器已设置,并且还必须根据实现决定是否调用所有监听器,或者是否在满足特定条件后停止调用。
尽管可以通过手动实现每个调用,但将其封装在某个地方是有意义的。
我能找到的唯一相关文章如下
即使 Microsoft 建议始终从 EventArgs
派生并总结了一些好的理由,但这一点绝不是本文的重点。 本文为了简单起见使用了函数。
源代码概述
可以非常轻松地构建一个通用的聚合调用程序。 首先,定义
public static R InvokeAndAggregateFunc<R>(Func<R> multiCast, bool execAll, R defaultRet,
R initAggregate, Func<R, R, R> aggregate, Func<R, bool> breakCondition)
Func<R> multiCast
是要调用的多播函数,其中R
是返回值的类型bool execAll
用于指定是否调用所有目标,或者如果满足breakCondition
,则停止调用R defaultRet
是在没有任何要调用的内容(即调用列表为空)时使用的默认返回值R initAggregate
是聚合函数的初始值Func<R,R,R>
是聚合函数,它接受两个参数,其中第一个是聚合值的当前值,第二个是当前调用的返回值Func<R,bool>
是中断条件,仅当参数execAll
设置为false
时才进行评估
因为它非常简单直接,所以我不会深入研究实现的细节,只是将代码作为参考添加
public static R InvokeAndAggregateFunc<R>(Func<R> multiCast, bool execAll, R defaultRet,
R initAggregate, Func<R, R, R> aggregate, Func<R, bool> breakCondition)
{
if (multiCast != null)
{
R ret = initAggregate;
foreach (Delegate d in multiCast.GetInvocationList())
{
Func<R> f = d as Func<R>;
R r = f.Invoke();
ret = aggregate(ret, r);
if (!execAll && breakCondition(ret))
break;
}
return ret;
}
else { }
return defaultRet;
}
您可能已经注意到,目前,其返回值将被聚合的函数不能有参数。 要添加此可能性,可以复制上述静态函数,并添加一个或多个泛型参数
public static R InvokeAndAggregateFunc<P1, P2, P3, P4, P5, R>(Func<P1, P2, P3, P4, P5,
R> multiCast, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, bool execAll, R defaultRet, R initAggregate,
Func<R, R, R> aggregate, Func<R, bool> breakCondition)
{
if (multiCast != null)
{
R ret = initAggregate;
foreach (Delegate d in multiCast.GetInvocationList())
{
Func<P1, P2, P3, P4, P5, R> f = d as Func<P1, P2, P3, P4, P5, R>;
R r = f.Invoke(p1, p2, p3, p4, p5);
ret = aggregate(ret, r);
if (breakCondition(ret) && !execAll)
break;
}
return ret;
}
else { }
return defaultRet;
}
使用代码
使用代码很简单。 如果您有多播,例如
Func<bool> f1 = new Func<bool>(() => true);
f1 += new Func<bool>(() => false);
并且想知道所有目标是否都返回 true
,您可以这样做
bool bAnd = InvokeAndAggregateFunc(f1, true, false, true, (v1, v2) => v1 && v2, v => !v);
如果您想知道其中至少一个是否返回 true
(并且如果不需要结果,则不调用所有目标),您可以这样做
bool bOr = InvokeAndAggregateFunc(f1, false, false, false, (v1, v2) => v1 || v2, v => v);
关注点
本文未涵盖异常和性能,以及所有不同类型的多播表示。
历史
- 2012/09/05:提交第一个版本。