2015-02-22 5 views
1

Я пытаюсь создать базовый класс, который работает как конечный автомат и который может принимать любой тип перечисления:Пользовательский словарь TryGetValue не найти ключи

public class BaseFSM <T> where T : struct, IConvertible 
{ 

    //Basic class that denote the transition between one state and another 
    public class StateTransition 
    { 
     public T currentState { get; set; } 
     public T nextState { get; set; }   

     //StateTransition Constructor 
     public StateTransition(T currentState, T nextState) 
     { 
      this.currentState = currentState; 
      this.nextState = nextState; 
     } 

     public override int GetHashCode() 
     { 
      return 17 + 31 * this.currentState.GetHashCode() + 31 * this.nextState.GetHashCode();; 
     } 

     public override bool Equals(object obj) 
     { 
      StateTransition other = obj as StateTransition; 
      return other != null && this.currentState as Enum == other.currentState as Enum && this.nextState as Enum == other.nextState as Enum; 
     } 
    } 

    protected Dictionary<StateTransition, T> transitions; //All the transitions inside the FSM 
    public T currentState; 
    public T previusState; 

    protected BaseFSM() { 
     // Throw Exception on static initialization if the given type isn't an enum. 
     if(!typeof (T).IsEnum) 
      throw new Exception(typeof(T).FullName + " is not an enum type."); 
    } 

    private T GetNext(T next) 
    { 
     StateTransition transition = new StateTransition(currentState, next); 
     T nextState; 
     if (!transitions.TryGetValue(transition, out nextState)) 
      throw new Exception("Invalid transition: " + currentState + " -> " + next); 
     return nextState; 


    } 
} 

Как вы можете видеть, я определил как GetHashCode() и Equals (объект obj). Это моя реализация моего класса ребенка:

public class FSMPlayer : BaseFSM<PlayerState> 
{ 
    public FSMPlayer() : base() 
    {   
     this.currentState = PlayerState.Idle; 
     this.transitions = new Dictionary<StateTransition, PlayerState> 
     { 
      { new StateTransition(PlayerState.Idle, PlayerState.Run), PlayerState.Run }, //0 
      { new StateTransition(PlayerState.Run, PlayerState.Jump), PlayerState.Jump }, //1 
     }; 
    } 
} 

Как вы можете увидеть в моем дочернем классе я использую мой PlayerState Enum для определения переходов между состояниями. Проблема в том, что я пытаюсь использовать функцию getNext, потому что TryGetValue всегда возвращает false. Функция GetHashCode работает хорошо, поэтому я не могу понять, где проблема. Спасибо.

ответ

3

Проблема здесь:

this.currentState as Enum == other.currentState as Enum 

Enum является ссылочным типом, поэтому ваше перечисление получает коробку в (новый, уникальный) объект. В результате он больше не сравнивается с любым другим экземпляром в коробке.

enum типа делают правильные вещи для подмены Equals, хотя (как @hvd так правильно указывает), так что вы можете просто сделать

this.currentState.Equals(other.currentState) 
+2

Не будет работать простой 'this.currentState.Equals (other.currentState)'? – hvd

+0

@ hvd: да, абсолютно, я глуп. (Кроме того, перечисления не реализуют 'IComparable ', но они реализуют 'IComparable'.) Если вы хотите опубликовать это как ответ самостоятельно, я удалю свой. –

+0

Вы действительно определили реальную проблему, и вы действительно опубликовали правильный способ ее решения. Я рад просто ответить на ваш вопрос. :) – hvd

0

Вы также можете прочитать ответы на этот вопрос, чтобы узнать, почему вызов getHashCode в перечислении не рекомендуется.

Using GetHashCode for getting Enum int value

+0

Это прекрасно. Ответ в этом вопросе обсуждает, почему вы не должны полагаться на 'GetHashCode()' для возврата числового значения перечисления. Если вы на самом деле используете его для хеширования (и поэтому не полагаетесь на это свойство вообще), нет проблем. –

Смежные вопросы