views:

138

answers:

3

Hi,

I have created a console for my game in XNA and I have a delegate for when a command has been entered. At the moment the delegate is returning a bool value. I have declared an event inside the Console class (which returns false) and then subscribed to this event from other classes. The idea is, if none of the classes that subscribe to this event return true then it is assumed that the user has entered an invalid command. However if at least one of the subscribed classes returns true then the command is assumed to be valid.

At the moment, only one class is taken into account for returning true or false, is there a way I can look at the return values of all the subscribing classes then OR their result?

Thanks,

+5  A: 

From within the class that declares the event, you can retrieve the invocation-list of the event (assuming a field-like event). Invoking each of them individually will allow you to inspect the return value of each subscriber to the event.

For example:

public event Func<bool> MyEvent = delegate { return false; };

...     

private bool EmitMyEventAndReturnIfAnySubscriberReturnsTrue()
{
    return MyEvent.GetInvocationList()
                  .Cast<Func<bool>>()
                  .Select(method => method()) 
                  .ToList() //Warning: Has side-effects
                  .Any(ret => ret);
}

In this example, every subscriber is informed of the event - no short-circuit occurs if any of them responds affirmatively. This behaviour can be easily changed if desired by removing the call to ToList().

To be honest though, I don't really like events that return values; their semantics are not obvious to subscribers. I would change the design if at all possible.

EDIT: Corrected error in forcing full execution of the sequence based on Timwi's comment.

Ani
Wouldn't have occurred to me to use LINQ for this. Nice answer.
Kirk Woll
I looked at two different designs and this was the cleaner of both of them. And its quite easy to understand and implement. I am not familiar with LinQ but I did find an alternative solution as I described below.
Dave
@Dave: The alternative solution you've posted has the same idea as my answer - the trick is to access the invocation-list. Great.
Ani
@Kirk Woll: Thanks. I was wondering if I shouldn't use LINQ for this because of the side-effecting nature of the query, but I figured this wasn't an important aspect of this question, so went ahead with it.
Ani
@Ani: No its terrific, the more solutions the more we learn :-)
Dave
+2  A: 

I figured it out just as I posed this question :P

bool handled = false;
foreach (Delegate d in CommandProcessed.GetInvocationList())
    handled |= (bool) d.DynamicInvoke (gameTime, command.ToString());

if (!handled) { } // Command Unrecognized

Where CommandProcessed is my event that classes subscribe to.
My delegate takes two arguments: gametime and command string.

Dave
Yep - I had to solve this same problem recently for chained validation. The bitwise or operator doesn't short-circuit like the boolean or operator does.
arootbeer
A: 

I agree with the problem about events returning values as it seems off to me as well. However an alternative is you could create a new class CommandEventArgs:

class CommandEventArgs : EventArgs
{
    public DateTime GameTime {get; set; }
    public string Command {get; set;}
    public bool Valid {get; set;}
}

then use an instance of this method to invoke the event and then if the listener recognizes the command have it set Valid to true. Therefore if Valid is still false after invocation you know the command was unrecognized.

This is how .NET handles keyboard events you set KeyEventArgs.Handled to true to suppress any further action to happen with the event.

James J. Regan IV