一つの状態機の実現


話を多くしないで、まずコードを見ます。

interface IState
 {
  string Name { get; set; }
  //    
  IList<IState> Nexts { get; set; }
  Func<IState /*this*/, IState /*next*/> Selector { get; set; }
  
 }
 class State : IState
 {
  public string Name { get; set; } = "State";

  IList<IState> IState.Nexts { get; set; } = new List<IState>();
  public Func<IState, IState> Selector { get; set; }
 }
状態は比較的簡単で、一つのNameマーク、一つの後件状態リスト、そして一つの状態選択器です。
例えば状態aは、状態b、c、dに移行することができ、セレクタはその中の一つです。どのように選ぶかは、実際のセレクタをユーザーに定義させます。

delegate bool HandleType<T>(IState current, IState previous,ref T value);
 interface IContext<T> : IEnumerator<T>, IEnumerable<T>
 {
  //data
  T Value { get; set; }
  //    
  IDictionary<Tuple<IState/*this*/, IState/*previous*/>, HandleType<T>> Handles { get; set; }
  IState CurrentState { get; set; }
  bool transition(IState next);
 }
状態種類Stateのフォロー状態と違って、文脈系Contectは前の状態に注目します。新しい状態にジャンプすると、このプロセスは現在の状態に応じて異なるポリシーを実行します。例えば状態cに入りたいですが、現在の状態によってはa、b、dと異なる処理プログラムがあります。このような移行処理プログラムは、一対一で対応していますので、Tuple<進入の状態、現在の状態>でジャンプチェーンを記述しています。そしてDictionaryで関連の処理プログラムを縛ります。
文脈はT Valueデータを持っていますが、どうやってこのデータを処理しますか?私はrefパラメータを通して処理プログラムに伝達します。私はIStateが文脈の構造に関心を持ちたくないので、実際のデータT valueだけに関心を持つ必要があります。
コンテキストは、データと現在の状態を保存し、その後、トランジットを介してユーザに状態の移行を制御させます。この中にはリピートがあります。IStateはセレクタがあるので、状態をコントロールして移動しました。なぜこのように処理しますか?ジャンプシーケンスを構築するためです。IEnumeratorとIEnumerableインターフェースを導入して、状態はセレクタの作用で自動的にジャンプできます。結果シーケンスをforeachで読みます。

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 IEnumerator.Current => (this as IContext<T>).Value;
  bool IContext<T>.transition(IState next)
  {
   IContext<T> context= this as IContext<T>;
   if (context.CurrentState == null || context.CurrentState.Nexts.Contains(next))
   {
    //    
    var key = Tuple.Create(next, context.CurrentState);
    if (context.Handles.ContainsKey(key) && context.Handles[key] !=null)
     if (!context.Handles[key](next, context.CurrentState,ref this.data))
      return false;

    context.CurrentState = next;
    return true;
   }
   return false;
  }
  bool IEnumerator.MoveNext()
  {
   //    
   IContext<T> context = this as IContext<T>;
   IState current = context.CurrentState; 
   if (current == null)
    throw new Exception("        ");
   if (context.CurrentState.Selector != null)
   {
    IState next= context.CurrentState.Selector(context.CurrentState);
    return context.transition(next);
   }
   return false;
  }
  void IEnumerator.Reset()
  {
   throw new NotImplementedException();
  }
  #region IDisposable Support
  private bool disposedValue = false; //        
  protected virtual void Dispose(bool disposing)
  {
   if (!disposedValue)
   {
    if (disposing)
    {
     // TODO:       (    )。
    }
    // TODO:         (      )            。
    // TODO:          null。
    disposedValue = true;
   }
  }
  // TODO:      Dispose(bool disposing)                      。
  // ~Context() {
  // //        。          Dispose(bool disposing)  。
  // Dispose(false);
  // }
  //                。
  void IDisposable.Dispose()
  {
   //        。          Dispose(bool disposing)  。
   Dispose(true);
   // TODO:               ,        。
   // GC.SuppressFinalize(this);
  }
  IEnumerator<T> IEnumerable<T>.GetEnumerator()
  {
   return this;
  }
  IEnumerator IEnumerable.GetEnumerator()
  {
   return this;
  }
  #endregion
 }
ポイントはtransion関数とMoveNext関数です。

bool IContext<T>.transition(IState next)
  {
   IContext<T> context= this as IContext<T>;
   if (context.CurrentState == null || context.CurrentState.Nexts.Contains(next))
   {
    //    
    var key = Tuple.Create(next, context.CurrentState);
    if (context.Handles.ContainsKey(key) && context.Handles[key] !=null)
     if (!context.Handles[key](next, context.CurrentState,ref this.data))
      return false;
    context.CurrentState = next;
    return true;
   }
   return false;
  }
やることも簡単です。前の処理プログラムを呼び出して、処理が成功すれば状態を変えます。そうでないと退出します。

bool IEnumerator.MoveNext()
  {
   //    
   IContext<T> context = this as IContext<T>;
   IState current = context.CurrentState; 
   if (current == null)
    throw new Exception("        ");
   if (context.CurrentState.Selector != null)
   {
    IState next= context.CurrentState.Selector(context.CurrentState);
    return context.transition(next);
   }
   return false;
  }
MoveNextはセレクタによって次の状態を選択します。
総じて言えば、私のこの状態機の実現はただの枠組みであり、機能はあまりないですが、状態転換カタログのツリーを作成しやすいと思います。
ユーザはまずステータスセットを作成し、ディレクトリツリー構造を作成します。私の実装は、ユーザがディレクトリツリー、前のプロセッサ、後のセレクタの三つの部分を別々に構築するので、比較的粗いです。テストコードを作成する時、9つの状態の網状構造を書きましたが、結果は目まぐるしいです。統一できたらもっといいと思います。
注目すべきは最初の状態と最後の状態の構造です。そうでないと停止できなくなり、デッドサイクルに埋め込まれます。

//    
//---------    ---------
string mess = "";//3   
IState s3 = new State() { Name = "s3" };
//2   
IState s2 = new State() { Name = "s2" };
//1   
IState s1 = new State() { Name = "s1" };
//---------    ---------   
s1.Nexts = new List<IState> { s2, s3 };   
s2.Nexts = new List<IState> { s1, s3 };   
s3.Nexts = new List<IState> { }; //  end  
//---------   ---------    
//transition   
IContext<int> cont = new Context<int> { CurrentState=s1};//begin   
cont.Value = 0;
//---------     --------- 
HandleType<int> funcLaji = (IState current, IState previous, ref int v) => { mess += $"{current.Name}:  {previous.Name}
"; v++; return true; }; //1 cont.Handles.Add(Tuple.Create(s1 , default(IState)), funcLaji); cont.Handles.Add(Tuple.Create(s1, s2), funcLaji); //2 cont.Handles.Add(Tuple.Create(s2, s1), funcLaji); //3 cont.Handles.Add(Tuple.Create(s3, s1), funcLaji); cont.Handles.Add(Tuple.Create(s3, s2), funcLaji); //--------- --------- var rval = new Random(); Func<int,int> round = x => rval.Next(x); s1.Selector = st => round(2)==0? s2:s3; s2.Selector = st => round(2)==0? s1:s3;
構造が終わったら、この状態機が使えます。

//        
mess += "     :
------------------------
"; foreach (var stor in cont) mess+=$" :{stor}
"; // mess += "

------------------------
"; cont.transition(s1); cont.transition(s2); cont.transition(s3);
以上が本文の全部です。本文の内容は皆さんの学習や仕事に一定の助けをもたらしてくれると同時に、私達を応援してください。