Without further ado, look at the code:
interface IState { string Name { get; set; } //Result processing IList<IState> Nexts { get; set; } Func<IState /*this*/, IState /*next*/> Selector { get; set; } } class State : IState { public string Name { get; set; } = "State"; IList<IState> { get; set; } = new List<IState>(); public Func<IState, IState> Selector { get; set; } }
The status is relatively simple, a Name identifier, a back-piece status list, and then a status selector.
For example, state a can be transferred to state b, c, d, and then the selector is one of them. As for how to choose, let the user define the actual selector.
delegate bool HandleType<T>(IState current, IState previous,ref T value); interface IContext<T> : IEnumerator<T>, IEnumerable<T> { //data T Value { get; set; } //Previous parts processing IDictionary<Tuple<IState/*this*/, IState/*previous*/>, HandleType<T>> Handles { get; set; } IState CurrentState { get; set; } bool transition(IState next); }
Unlike the state class State that focuses on the backpack status, the context class Context focuses on the frontpack status. When jumping to a new state, different strategies must be implemented according to the current state in the process. For example, if you want to enter state c, there are different processing programs according to the current state a, b, and d. This transfer handler corresponds one by one, so Tuple<entered state, current state> is used to describe a jump chain. Then use Dictionary to bundle the relevant handlers.
The context will carry T Value data. How to deal with this data? I passed the ref parameter to the handler. Because I don't want IState to care about the context construction, it just needs to focus on the actual data T value;
The context saves the data and the current state, and then allows the user to control the transition of the state through transiton. There is a duplication here because IState has a selector to control state transitions. Why do we need to deal with this? I'm trying to construct a jump sequence. Introduce IEnumerator and IEnumerable interfaces, but the state can automatically jump under the action of the selector, and then use foreach to read the result sequence (I just don't know what it is useful).
class Context<T> : IContext<T> { T data; T IContext<T>.Value { get=>data ; set=>data = value; } IDictionary<Tuple<IState, IState>, HandleType<T>> IContext<T>.Handles { get; set; } = new Dictionary<Tuple<IState, IState>, HandleType<T>>(); public IState CurrentState { get; set;} T IEnumerator<T>.Current => (this as IContext<T>).Value ; object => (this as IContext<T>).Value; bool IContext<T>.transition(IState next) { IContext<T> context= this as IContext<T>; if ( == null || (next)) { //Previous parts processing var key = (next, ); if ((key) && [key] !=null) if () return false; = next; return true; } return false; } bool () { //Result processing IContext<T> context = this as IContext<T>; IState current = ; if (current == null) throw new Exception("The initial state must be set"); if ( != null) { IState next= (); return (next); } return false; } void () { throw new NotImplementedException(); } #region IDisposable Support private bool disposedValue = false; // To detect redundant calls protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { // TODO: Release managed state (managed object). } // TODO: Free unmanaged resources (unmanaged objects) and replace the finalizer in the following content. // TODO: Set large fields to null. disposedValue = true; } } // TODO: Replace the finalizer only if the above Dispose(bool disposing) has code to free unmanaged resources. // ~Context() { // // Do not change this code. Put the cleaning code into the above Dispose(bool disposing) . // Dispose(false); // } // Add this code to correctly implement disposable mode. void () { // Do not change this code. Put the cleaning code into the above Dispose(bool disposing) . Dispose(true); // TODO: If the finalizer is replaced in the above content, uncomment the following line. // (this); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return this; } IEnumerator () { return this; } #endregion }
Focus on transition function and MoveNext function.
bool IContext<T>.transition(IState next) { IContext<T> context= this as IContext<T>; if ( == null || (next)) { //Previous parts processing var key = (next, ); if ((key) && [key] !=null) if () return false; = next; return true; } return false; }
What you do is also very simple, which is to call the pre-part handler. If the processing is successful, it will be transferred to the state, otherwise it will exit.
bool () { //Result processing IContext<T> context = this as IContext<T>; IState current = ; if (current == null) throw new Exception("The initial state must be set"); if ( != null) { IState next= (); return (next); } return false; }
MoveNext selects the next state through the selector.
In general, my state machine implementation is just a framework and has no functions, but I feel it is easier to write a state transfer directory tree.
The user first needs to create a set of states and then create a directory tree structure. My implementation is relatively rough because the user needs to build three parts: directory tree, front-part processor, and back-part selector. When writing the test code, I wrote the mesh structure of 9 states, and the result was a bit dazzled. It would probably be better if it could be unified.
What we need to pay attention to is the construction of the first state and the last state, otherwise it will not be able to shut down and be embedded in a dead loop.
//Test code//-----------------------------string mess = "";//3 IState s3 = new State() { Name = "s3" }; //2 IState s2 = new State() { Name = "s2" }; //1 IState s1 = new State() { Name = "s1" }; //----------------------------------------------------------------------------------------------------------------------------- = new List<IState> { s2, s3 }; = new List<IState> { s1, s3 }; = new List<IState> { }; // Pay attention to the end writing method//-----------------------------------------------------------------------------------------------------------------------------//transition IContext<int> cont = new Context<int> { CurrentState=s1};//begin = 0; //-----------------------------------------------------------------------------------------------------------------------------HandleType<int> funcLaji = (IState current, IState previous, ref int v) => { mess += $"{}:Rubbish{}\n"; v++; return true; }; //1 ((s1 , default(IState)), funcLaji); ((s1, s2), funcLaji); //2 ((s2, s1), funcLaji); //3 ((s3, s1), funcLaji); ((s3, s2), funcLaji); //-----------------------------------------------------------------------------------------------------------------------------var rval = new Random(); Func<int,int> round = x => (x); = st => round(2)==0? s2:s3; = st => round(2)==0? s1:s3;
After the construction is completed, this state machine can be used.
//Selector jumpmess += "Selector Jump:\n---------------------------------------------------------------------------------------------------------------------; foreach (var stor in cont) mess+=$"Number of state transitions:{stor}\n"; //Directly control jumpmess += "\nDirect control status jump:\n-------------------------------------------------------------------------------------------------------------------; (s1); (s2); (s3);
The above is all the content of this article. I hope that the content of this article will help you study or work. I also hope to support me more!