Как зарегистрировать слушателя для действий, хранящихся в словаре - PullRequest
0 голосов
/ 19 апреля 2020

Для игры, которую я разрабатываю, я отслеживаю GameState, чтобы определить, какие системы должны быть активными. Чтобы позволить системам регистрировать себя в изменениях состояния, я написал следующий код:

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

Когда состояние изменяется, оно вызывает соответствующее действие, ища ключ GameState в словаре _stateChangeActions. Вот странное поведение, которое я не могу понять. Если я подписываюсь на действие, используя _stateChangeActions[key] += ListenerMethod;, оно запускается правильно. Но если я подписываюсь на поле publi c stati c, например, OnDefaultStateChange += ListenerMethod;, и запускаю действие через словарь, это как если бы слушателей не было.

Я не смог чтобы выяснить, почему это происходит. Примечание: я использую Unity Engine, и эта проблема не блокирует меня, мне просто любопытно.

1 Ответ

2 голосов
/ 19 апреля 2020

Ответ на ваш вопрос

OnDefaultStateChange и _stateChangeActions не имеют никакого отношения друг к другу, кроме того, что вы используете OnDefaultStateChange для инициализации _stateChangeActions. Ваша строка с {GameState.Default, OnDefaultStateChange}, добавляет объект внутри OnDefaultStateChange в словарь и не ссылку , что означает, что _stateChangeActions[GameState.Default] не совпадает с OnDefaultStateChange.

Пример, показывающий, что на самом деле происходит в вашей настройке:

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

Как видите, OnDefaultStateChange и _stateChangeActions работают как два независимых объекта, поэтому «подписка» на OnDefaultStateChange не делает этого нового подписчика доступным для _stateChangeActions.

Как решить вашу проблему

Я бы посоветовал вам использовать функции события в C#. Я немного догадываюсь о том, как вы на самом деле проверяете тип события, которое нужно запустить, но ваш класс обработки событий может выглядеть примерно так:

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

Чтобы подписаться на события, вы просто делаете то, что уже сделали do:

MyEventHandlerClass.DefaultStateChanged += ListenerMethod;

С помощью этой настройки вы можете подписаться или отписаться (-=) на события отовсюду.

...