For a game I'm developing I'm keeping track of a GameState to determine which systems should be active. To enable systems to register themselves to State changes, I've written the following code:
public static Action<State> OnDefaultStateChange;
public static Action<State> OnConstructionStateChange;
private static Dictionary<GameState, Action<State>> _stateChangeActions =
new Dictionary<GameState, Action<State>>()
{
{GameState.Default, OnDefaultStateChange},
{GameState.Construction, OnConstructionStateChange}
};
When a state is changed, it invokes the relevant action by looking up the GameState key in the _stateChangeActions dictionary.
Here's the strange behaviour that I can't understand.
If I subscribe to the action by using _stateChangeActions[key] += ListenerMethod;
, it invokes correctly. But if I subscribe on the public static field, e.g OnDefaultStateChange += ListenerMethod;
, and I invoke the action through the dictionary, it's as if there are no listeners.
I haven't been able to find out why this happens. Note: I'm using Unity Engine, and this issue isn't blocking me, I'm just curious.
OnDefaultStateChange
and _stateChangeActions
have no relation to each other, other than the fact you use OnDefaultStateChange
to initialize _stateChangeActions
.
Your line with {GameState.Default, OnDefaultStateChange},
adds the object inside OnDefaultStateChange
to the dictionary and not the reference, which means that _stateChangeActions[GameState.Default]
is not the same as OnDefaultStateChange
.
An example to show what is actually going on in your setup:
var state = new { LivesLeft = 2, ShirtColor = "brown" };
// Corresponds to 'OnDefaultStateChange'
Action<State> someAction = (s) =>
{
Console.WriteLine("Lives: " + s.LivesLeft);
};
// Corresponds to '_stateChangeActions'
Action<State> copyOfSomeAction = someAction;
// Subscribe to "OnDefaultStateChange"
someAction += (s) =>
{
Console.WriteLine("Shirt color: " + s.ShirtColor);
};
// 'someAction' is longer equal to 'copyOfSomeAction' since 'someAction'
// has been replaced with a new Action which produces the result from two other
// Actions.
someAction(state);
// Output:
// Lives: 2
// Shirt color: brown
copyOfSomeAction(state);
// Output:
// Lives: 2
As you can see OnDefaultStateChange
and _stateChangeActions
works as two independent objects, so "subscribing" to OnDefaultStateChange
doesn't make that new subscriber available to _stateChangeActions
.
I would suggest you make use of the event features in C#. I'm guessing a little on how you actually check the type of event to fire, but your event handling class could look something like this:
// MyEventHandlerClass.cs
public delegate void StateChangedEventHandler(object sender, State state);
public static event StateChangedEventHandler DefaultStateChanged;
public static event StateChangedEventHandler ConstructionStateChanged;
private static FireNewStateChangeEvent(State state) {
switch (state.StateChangeType)
{
case GameState.Default:
DefaultStateChanged.Invoke(this, state);
case GameState.Construction:
ConstructionStateChanged.Invoke(this, state);
}
}
To subscribe to events you simply do pretty much like you already do:
MyEventHandlerClass.DefaultStateChanged += ListenerMethod;
With this setup you can subscribe or unsubscribe (-=
) to events from wherever.