Click here to Skip to main content
Click here to Skip to main content

Invoke and Aggregate Multicasts

By , 28 Feb 2013
Rate this:
Please Sign up or sign in to vote.

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

zaphoed
Software Developer
Switzerland Switzerland
experienced in asm/c/c++/c#. Less experienced in Python, Lua, Shell and stuff like ASP, PHP, HTML ...
coding for windows, macOS, iOS and linux
just4fun: ogre3d

Comments and Discussions

 
GeneralWhy don't you use the pattern that Microsoft already use? PinmemberJohn Brett5-Sep-12 21:50 
GeneralRe: Why don't you use the pattern that Microsoft already use? Pinmemberzaphoed5-Sep-12 22:30 
GeneralRe: Why don't you use the pattern that Microsoft already use? PinmemberJohn Brett5-Sep-12 22:49 
GeneralRe: Why don't you use the pattern that Microsoft already use? Pinmemberzaphoed5-Sep-12 23:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140421.2 | Last Updated 28 Feb 2013
Article Copyright 2012 by zaphoed
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid