Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Invoke and Aggregate Multicasts

0.00/5 (No votes)
28 Feb 2013 1  
A generic approach to aggregate return values of multicast functions.

Introduction

The following article shows one possibility to aggregate return values of multicasters in C#.

Since events, delegates, multicasting, and everything related has been discussed many times before, I will completely ignore these topics and assume that the reader is familiar with them.

Background 

I often have had the need to safely (and synchronously) invoke a (possible unassigned) event handler and aggregate the return values.

A very common pattern there is the before- and afterEdit pattern, where listeners have the possibility to cancel the edit. MS, for example, does provide a Cancel property in some of their EventArgs based event argument classes, for example the "Selecting" event of the WinForms TabControl has TabControlCancelEventArgs with a boolean Cancel property.

Because there may be many listeners which potentially set the Cancel property to true, the invoker has to check if at least one of them is set and also has to decide, depending on the implementation, if all gets invoked or if the invocation stops as soon as a certain condition is met.

Although, it's possible to implement every invocation by hand, it could make sense to encapsulate it somewhere. 

The only closely related article I could find is the following:

Even if Microsoft suggests to always derive from EventArgs and sums some good reasons, this point is in no case the focus of this article. The article uses Functions for the sake of simplicity.

Source code overview

A generic and aggregating invoker can be built very easy. First, the definition:

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 is the multicast function to invoke where R is the type of the return value
  • bool execAll is used to specify if all targets will be invoked or if the invocations are stopped as soon as the breakCondition is met 
  • R defaultRet is the default return value for the case when there isn't anything to invoke (i.e., the invocation list is empty)
  • R initAggregate is the initial value for the aggregation function
  • Func<R,R,R> is the aggregation function, taking two parameters, where the first is the current value of the aggregated value and the second is the return value of the current invocation
  • Func<R,bool> is the break condition which is only evaluated if the parameter execAll is set to false

Because it's so easy and straightforward, I'm not going into the details of the implementation but just add the code as reference:

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;
}

You may have noticed at this point that the functions whose return values will be aggregated cannot have parameters at this time. To add this possibility, the above static function can be duplicated and one or more generic parameter can be added:

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;
}

Using the code

Using the code is simple. If you've got multicast, for example:

Func<bool> f1 = new Func<bool>(() => true);
f1 += new Func<bool>(() => false);

and like to know if all targets return true, you could do the following:

bool bAnd = InvokeAndAggregateFunc(f1, true, false, true, (v1, v2) => v1 && v2, v => !v);

and if you like to know if at least one of them returns true (and don't invoke them all if not needed to get the result), you could do the following:

bool bOr = InvokeAndAggregateFunc(f1, false, false, false, (v1, v2) => v1 || v2, v => v);

Points of Interest

Exceptions and performance are not covered by the article, as are all different kinds of multicast representations.

History 

  • 2012/09/05: First version submitted.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here