0

I am struggling with a specific dictionary for changing states of an object.

The idea is that I have class Process, which is created everytime user submit something to be process. Process has ID, some other info and CurrentState. Possible CurrentStates are defined in StateEnum class and contains states like "Pending, Processing, Completed, Error" etc. After that, there are some tasks/commands, that user can perform - each of this task is defined as a class as well and contains ID of process and Execute method, that changes the state of object. These tasks inherits from interface ITask.

Thing is, that I require some kind of state-machine, that would verify whether it is possible to move from state "Pending" to state "Processing" using task "ResumeTask" for example. For this verification, I have created class State with a dictionary and its object is created each time the Process is created.

Problem is, that the dictionary is not working properly. Let me show the code (I have created simple Console Application just to demonstrate the problem) with some comments and explain what is happening:

ITask interface

public interface ITask
{

    int IntParam1 { get; set; }

    void Execute(Process proc);

}

ResumeTask class (I have also PauseTask and other Tasks)

class ResumeTask : ITask
{

    int intParam1;
    public int IntParam1
    {
        get { return intParam1; }
        set { intParam1 = value; }
    }


    public void Execute(Process proc)
    {
        ResumeTask resume = new ResumeTask();
        proc.ChangeState(resume);

        //code below not working - is there a way how to make this 
        //work instead of creating new empty class above?
        //proc.ChangeState(this);
    }

    public override int GetHashCode()
    {
        return 17 + 31 * intParam1.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        ResumeTask other = obj as ResumeTask;
        return other != null && this.intParam1 == other.intParam1;
    }


}

Process class

public class Process
{

    State stat;


    private int processID;
    public int ProcessID
    {
        get { return processID; }
        private set { processID = value; }
    }

    private StateEnum state;
    public StateEnum State
    {
        get { return state; }
        set { state = value; }
    }

    public readonly int userID;
    public int UserID
    {
        get { return userID; }
    }

    public Process(int processID, int userID)
    {
        this.processID = processID;
        this.userID = userID;
        stat = new State();
        this.state = stat.CurrentState;
    }

    public void ChangeState(ITask task)
    {

        this.state = stat.MoveNext(task);

    }

}

State class

class State
{

    private class StateTransition
    {
        public StateEnum State;
        public ITask Task;

        public StateTransition(StateEnum state, ITask task)
        {
            State = state;
            Task = task;
        }

        public override int GetHashCode()
        {
            return 17 + 31 * State.GetHashCode() + 31 * Task.GetHashCode();
        }

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


    public StateEnum CurrentState;

    Dictionary<StateTransition, StateEnum> transitions;

    ITask pause = new PauseTask();
    ITask resume = new ResumeTask();

    ITask resumenew = new ResumeTask();


    public State()
    {

        CurrentState = StateEnum.Pending;
        transitions = new Dictionary<StateTransition, StateEnum>
        {
            { new StateTransition(StateEnum.Processing, pause), StateEnum.Pending },
            { new StateTransition(StateEnum.Pending, resume), StateEnum.Processing }
        };
    }


    public StateEnum MoveNext(ITask task)
    {
        StateTransition transition = new StateTransition(CurrentState, task);
        StateEnum nextState;
        if (!transitions.TryGetValue(transition, out nextState))
        {
            return StateEnum.Error;
        }
        else
        {
            return nextState;
        }
    }

}

StateEnum enum

public enum StateEnum
{

    Pending,
    Processing,
    Error,
    Processed,
    Downloaded

}

Program class

class Program
{
    static void Main(string[] args)
    {

        Process proc = new Process(1, 20);

        ResumeTask resumetask = new ResumeTask();
        resumetask.IntParam1 = 1;
        resumetask.Execute(proc);

        Console.WriteLine(Convert.ToString(proc.ProcessState));
        Console.ReadLine();


    }
}

Lets quickly go through some important points:

  1. In all tasks, I have overridenn Equals and GetHashCode methods, because Ive learned that is has to be done like it, if I want to use object in dictionary.
  2. If you notice the execute method - problem is, that when I was testing it before on a simplier example, I had to pass new object without any values, in order to get some succesfuly results - maybe thats because in dictionary, I have empty class but I was passing class with intParam1 set and try to compare them?
  3. In Process class, I simply create new State object and acquire CurrentState from it. Also, the method MoveNext simply pass along the input parameter from Execute() method in Task
  4. In State class, there is the biggest issue (I think). As you can see, I had to create another type "StateTransition" (this is borrowed from state-machine example here on StackOverflow) because in real application, there are more Tasks that could be performed on certain state - and in order to have unique keys in dictionary, I had to do it like that. So basicaly I create the type, again override the Equald and GetHashCode and after that, I create dictionary, in which StateTransition serves as a key. I had to create empty objects of Tasks (pause, resume) in order to put them in dictionary - I am not sure whether this is the right approach, but it was working to some point. After that, I try to find a value for the key, which consists of a CurrentState in Process and task, that I passed in Execute() method before. But the result is always "Error".
  5. I have tried several things - I tried to create 2 simple dictionaries:

    transitions<ITask, StateEnum>;
    availableTasks<StateEnum,ITask>;

    and it was working fine - I checked them in MoveNext method and was able to make valid move - the problem was, that with this approach, I couldn create the scenario, in which I need to have multiple tasks available for 1 state.

  6. I have also tried to experiment with the declaration of

StateTransition transition = new StateTransition(CurrentState, task);

In MoveNext method in State class - for example if I put object "resume" (which was created previously in order to add it in dictionary) instead of "task" parameter, it is working - because I am basicaly comparing 2 same objects. But, as you can see, I have also tried to define object

ITask resumenew = new ResumeTask();

and pass it again instead of "task" parameter - it was not working.

To sum it up - is this generaly bad approach or do I have some small mistake somewhere, that I am not aware of? I hope this question is not too chaotic - in case on any question, I can edit it or answer in comments. Thank you for any help.


EDIT: If I leave the rest as it is and just change State class like this:

class State
{




    public StateEnum CurrentState;

    Dictionary<ITask, StateEnum> transitions;

    ITask pause = new PauseTask();
    ITask resume = new ResumeTask();

    public State()
    {

        CurrentState = StateEnum.Pending;
        transitions = new Dictionary<ITask, StateEnum>
        {
            {pause, StateEnum.Pending },
            {resume, StateEnum.Processing }
        };
    }


    public StateEnum MoveNext(ITask task)
    {
        StateEnum nextState;
        if (!transitions.TryGetValue(task, out nextState))
        {
            return StateEnum.Error;
        }
        else
        {
            return nextState;
        }
    }

}

It is working - if state is StateEnum.Pending and I pass task ResumeTask resumetask = new ResumeTask() in MoveNext() method, it will return me StateEnum.Processing, which is correct.

So the "simple" object key dictionary is working, but when I encapsulate that object into another object, then despite of overriding Equals and GetHashCode again, I cant get a match (that implementation is shown in main post - State class).

Question is, why I cant get a match, if I am basicaly passing object of the same class.


EDIT2:

This was too complicated for the purpose I needed. I have tried different approach using List<KeyValuePair<Enum,Object>> since I dont need to change the dictionary after creating it and list has no problems with same key for more values. So far, it is working.

I can post the final code if needed.

JakubJ
  • 235
  • 4
  • 13
  • 2
    Narrow your problem in other cases this is "too broad question" for me – mybirthname Jan 28 '15 at 10:07
  • Im not sure what exactly do you mean, but basicaly I dont understand, why scenario with "simple" object-key dictionary is working, but scenario with object key (which consists of another object) is not working - such in my case. – JakubJ Jan 28 '15 at 10:11
  • 1
    @Jakub: Try to reduce your question. If you have an issue with dictionary then show us a small example that does work, and then a small example of what doesn't work. Also include a few lines saying why it doesn't work. – musefan Jan 28 '15 at 10:14
  • Your actual question is unclear, you start by saying you want to limit what choices can be changed to, and then you say is this a good approach? Does this code work? If not, what does it do and what should it do? Would it not just be enough to check the enum you are changing to has a higher value than your current one? – Sayse Jan 28 '15 at 10:15
  • WRT "sorry, I dont know how to add angle brackets "<" in these pieces of code": that's what the "{}" button is for in the editor. Simply type `Whatever`, select the text and apply the button. Or use [backticks](http://stackoverflow.com/editing-help). – BCdotWEB Jan 28 '15 at 10:16
  • Ok, I will create the example that is working fine and show differencies between the one I am trying to achieve. I will edit my post. – JakubJ Jan 28 '15 at 10:16
  • 1
    It's a really really bad idea to allow the properties that participate in `GetHashCode` to be mutable, because it means you can change them after they've been inserted into a Dictionary (or any other hash table-like structure) and corrupt the Dictionary. – spender Jan 28 '15 at 10:25
  • I have added a modified State class that is working fine. I can add more if it is not enough. – JakubJ Jan 28 '15 at 11:02
  • If any concurrent access propblem, you can use **ConcurrentDictionary*** for predefined locking mechanism.... – Veeramani Bala Jan 28 '15 at 11:26
  • I have tested it with only 1 active user (so only 1 access at a time) and it is not working anyway. – JakubJ Jan 28 '15 at 11:28
  • Yes it is too chaotic. You have a mutable HashKey - resumetask.IntParam1 = 1; - that is a no. And hash of an int is the int. Why return 17 + 31 * intParam1.GetHashCode();? – paparazzo Jan 28 '15 at 15:13
  • I was trying to use this apporach: http://stackoverflow.com/questions/5923767/simple-state-machine-example-in-c but you are right, in this example, there is only one parameter, so it is not neccesary. From what Ive learned, multiplying like that is used in order to prevent diagonal collision, while using more than 1 parameter: http://stackoverflow.com/questions/371328/why-is-it-important-to-override-gethashcode-when-equals-method-is-overridden . Also I have added 2nd edit to my main post - I have found different approach to solve this issue. – JakubJ Jan 28 '15 at 16:18
  • __Way__ Too Long To Read.. – TaW Jan 28 '15 at 18:28

0 Answers0