Net State Machine
v0.4.0
.NET 的狀態機構建立器庫。
以下範例顯示了狀態機的一些功能。
使用 Enderlook.StateMachine;public class Character{private static StateMachineFactory<States, Events, Character>?工廠;私有唯讀隨機rnd = new();私有唯讀StateMachine<狀態、事件、字元> stateMachine;私有int health = 100;私有int food = 100;私有枚舉States{Sleep,Play,GettingFood,Hunt, Gather,} private enum Events{HasFullHealth,LowHealth,IsHungry,IsNoLongerHungry,}public static async Task Main(){Character character = new();while (true){Console.Clear();///執行狀態機的更新並向其傳遞任意參數。不想傳遞一個參數可以刪除.With()方法呼叫。 $"狀態:{character.stateMachine.CurrentState}。");Console.WriteLine($"生命值:{character.health}。");Console.WriteLine($"食物:{character.food}。"); wait Task.Delay(10).ConfigureAwait(false);}}public Character(){// 建立狀態機的實例。 // 或者,如果你想將參數傳遞給狀態機的初始化,你可以這樣做: // stateMachine = GetStateMachineFactory().With(parameter).Create(this).
// 方法 `.With(parameter)` 可以根據需要連接任意多次。
// 模式 `stateMachine.With(p1).With(p2)...With(pn).SomeMethod(...)` 對於方法 `Fire()`、`FireImmediately()` 和 `Update 也有效() `.}private static StateMachineFactory<狀態、事件、字元> GetStateMachineFactory(){if (factory 不為空)返回工廠;StateMachineFactory<狀態、事件、字元>? factory_ = StateMachine<States, Events, Character>// 狀態機是從工廠創建的,這使得創建多個實例// 在CPU 和內存方面都更便宜,因為計算只完成一次並在創建的實例之間共享..CreateFactoryBuilder()//確定狀態機的初始狀態。 InitializationPolicy. Ignore)// 設定一個狀態..In(States.Sleep)// 每次進入該狀態時都會執行..OnEntry(() => Console.WriteLine("Going to bed."))// 每次都會執行我們退出此狀態..OnExit(() => Console.WriteLine("Getting up."))// 每次更新方法(Update() 或With<T>(T).Update())時執行// 所有事件都提供一個重載來傳遞接收者,因此可以在建構特定實例期間對其進行參數化。其進行參數化呼叫With<T>(T).Update()。 .. .), If(...) 和Do(...) 方法..OnUpdate(@this => @this.OnUpdateSleep()).On(Events.HasFullHealth)// 每次在this 中觸發此事件時執行state..Do(() => Console.WriteLine("Picktoys."))// 要轉換的新狀態..Goto(States.Play)// 或者,您可以在轉換期間配置事件執行策略。呼叫is true..If(@this => @this.IsVeryWounded())// 我們保持當前狀態,不執行 OnEntry 或 OnExit 委託..StaySelf()// 上面的方法是:// .OnEntryPolicy 的捷徑(TransitionPolicy.Ignore).OnExitPolicy(TransitionPolicy.Ignore).Goto(States.Sleep).// 如果我們想執行這些委託,我們可以使用:// .GotoSelf(false)// 這是://的捷徑.OnEntryPolicy(TransitionPolicy.ChildFirstWithCullingInclusive).OnExitPolicy(TransitionPolicy.ParentFirstWithCullingInclusive).Goto(States.Sleep).// 如果另外,我們想從其父狀態轉換(由於父狀態在執行本例)不是子狀態)我們可以這樣做:// .GotoSelf(true)// 這是// .OnEntryPolicy(TransitionPolicy.ChildFirst).OnExitPolicy(TransitionPolicy.ParentFirst).Goto(States.Sleep).// 的快捷方式否則,如果條件為true,則執行下一個呼叫..If(@this => @this.IsWounded()).Goto(States.Gather)// 否則無條件執行..Goto(States.Hunt)// 忽略此事件在此轉換中。方式.OnEntryPolicy(TransitionPolicy.Ignore).OnExitPolicy(TransitionPolicy.Ignore).Goto(States.Sleep)..In(States.Play).OnUpdate(@this => @this.OnUpdatePlay()). ) .If(@this => @this.IsWounded()).Goto(States.Gather).Goto(States.Hunt).In(States.GettingFood).OnEntry(() => Console.WriteLine("去尋找食物.")).OnExit(() => Console.WriteLine("Stop go for food.")).In(States.Gather)// 確定此狀態是另一個狀態的子狀態。 // 這表示OnUpdate 委託// 也取決於轉換期間配置的 OnEntryPolicy 和 OnExitPolicy, // 該狀態下訂閱的 OnEntry 和 OnExit 委託可能會在子狀態轉換期間運行..IsSubStateOf(States.GettingFood).OnUpdate ((字元@this,浮點參數) => @this.OnUpdateGather(參數)).On(Events.IsNoLongerHungry).If(@this => @this.IsWounded()).Goto(States.Sleep).Goto( States.Play). On(Events.HasFullHealth).Goto(States.Hunt).In(States.Hunt).IsSubStateOf(States.GettingFood).OnEntry(() => Console.WriteLine("鞠躬。")) .OnExit(() = > Console.WriteLine("放下弓。")).OnUpdate((字符@this,浮動參數) => @this.OnUpdateHunt(參數)).On(Events.IsNoLongerHungry).Goto(States .Sleep).On( Events.LowHealth).Goto(States.Sleep).Finalize();// 互鎖對於減少多執行緒情況下的記憶體使用很有用。如果從兩個不同的工廠建立兩個實例,它將比從同一工廠建立的兩個實例消耗更多的記憶體。 float lucky){food += (int)MathF.Round(rnd.Next(8) * lucky);if (food >= 100){食物= 100;stateMachine.Fire(Events.IsNoLongerHungry); // 或者,如果你想將參數傳遞給狀態機的初始化,你可以這樣做:
// stateMachine.With(paramter).Fire(Events.IsNoLongerHungry);}health -= (int)MathF.Round(rnd.Next(6) * (1 - 運氣));if (health <= 20)stateMachine. Fire(Events.LowHealth);}private void OnUpdateGather(float lucky){food += (int)MathF.Round(rnd.Next(3) * lucky);if (food >= 100){food = 100;stateMachine. Fire(Events.IsNoLongerHungry);}if (rnd.Next(1) % 1 == 0){health++;if (health >= 100){health = 100;stateMachine.Fire(Events.HasFullEvent);}}}} void OnUpdatePlay(){food -= 3;if (food <= 0){food = 0;stateMachine.Fire(Events.IsHungry);}}private void OnUpdateSleep(){health++;if (health >= 100){health = 100;stateMachine.Fire(Events.HasFullHealth);}食物-= 2;if (食物<= 0){食物= 0;stateMachine.Fire(Events.IsHungry);}}}public seal class StateMachine<TState, TEvent, TRecipient>where TState : notnullwhere TEvent : notnull{// 取得此狀態機的目前(子)狀態。 }/// 取得目前(子)狀態及其所有父狀態層次結構。 }/// Get 依照目前(子)狀態接受事件。 }/// 建立工廠 builder.public static StateMachineBuilder<TState, TEvent, TRecipient> CreateFactoryBuilder();/// 取得指定狀態的父狀態。 state, [NotNullWhen(true)] out TState?parentState);/// 取得指定狀態的父層次結構。如果state不是子狀態,則傳回空。指定狀態或指定狀態的(嵌套)子狀態。排隊運行。在執行該事件的回呼期間入隊,它們也將在該事件完成後執行。 /// 儲存可以傳遞給訂閱委託的參數。中的Fire(TEvent)相同,但包含可以傳遞給訂閱委託的所有儲存值。值。 / 儲存可以傳遞給回呼的參數。 TEvent, TRecipient>where TState : notnullwhere TEvent : notnull{// 使用以下命令建立已配置和初始化的狀態機此工廠提供的配置。傳遞給訂閱委託的參數。決定狀態機的初始狀態。初始化策略= ExecutionPolicy.ChildFirst);// / 新增狀態或載入先前新增的state.public StateBuilder<TState, TEvent, TRecipient> In(TState state);/// 使用builder.public StateMachineFactory<TState, TEvent, TRecipient > 以配置建立工廠();}public seal class StateBuilder<TState, TEvent, TRecipient> : IFinalizedwhere TState : notnullwhere TEvent : notnull{// 將呼叫轉送到StateMachineBuilder<TState, TEvent, TRecipient>.In(TState ). public StateBuilder<TState, TEvent, TRecipient> In(TState state);/// 轉送呼叫StateMachineBuilder<TState, TEvent, TRecipient>.Finalize();public StateMachineFactory<TState, TEvent, TRecipient> Finalize();//// 會此狀態標記為指定狀態的子狀態。 action); /// 與OnEntry(Action) 相同,但將接收者傳遞為參數。將任何參數傳遞給委託/// 如果沒有找到指定泛型參數傳遞的參數,則忽略該參數。 (Action<TRecipient>) 和OnEntry(Action<TParameter>).public StateBuilder<TState, TEvent, TRecipient> 的組合版本OnEntry<TParameter>(Action<TRecipient, TParameter> action);/// 確定一個從此狀態退出時執行的動作。 TRecipient> action);/// 與OnExit(Action) 相同,但將呼叫期間傳遞的與泛型參數類型相符的任何參數傳遞給委託。 public StateBuilder<TState, TEvent, TRecipient> OnExit<TParameter>(Action<TParameter> action);/// OnExit(Action<TRecipient>) 與OnExit(Action<TParameter>) 的組合版本.public StateBuilder<TState , TEvent, TRecipient> OnExit<TParameter>(Action<TRecipient, TParameter> action);/// 決定更新此狀態時要執行的動作。 Action) 相同,但將接收者作為參數傳遞。與泛型參數類型相符的呼叫。 )./// 如果沒有找到使用指定泛型參數傳遞的參數,則忽略該參數。行為在觸發指定事件期間執行。被觸發,它會拋出。這可以透過忽略呼叫來防止拋出異常。 : notnullwhere TEvent : notnull{// 增加一個子轉換,當委託傳回 true 時執行。 // 與If(Func<bool>) 相同,但將接收者作為參數傳遞。 ; /// 與If(Func<bool>) 相同,但將呼叫期間傳遞的任何與泛型參數類型相符的參數傳遞給委託。 TParent>> If<TParameter>(Func<TParameter, bool> Guard);/// If(Func<TRecipient, bool>) 和If(Func<TParameter, bool>).public TransitionBuilder<TState, TEvent, TRecipient, TransitionBuilderBuilder的組合版本<TState, TEvent, TRecipient, TParent>> If<TParameter>(Func<TParameter, bool> Guard);/// 確定引發事件時要執行的操作。 Do (Action action);/// 與Do(Action) 相同,但傳遞接收者為參數。 Action) 相同但將呼叫期間傳遞的與泛型參數類型相符的任何參數傳遞給委託。 , TParent> Do<TParameter >(Action<TParameter> action);/// Do(Action<TRecipient>) 和Do(Action<TParameter>).public TransitionBuilder<TState, TEvent, TRecipient, TParent> 的組合版本Do< TParameter>(Action<TRecipient , TParameter> action);/// 設定訂閱的委託在入口鉤子上如何執行的策略。 TEvent, TRecipient , TParent> OnEntryPolicy(TransitionPolicy policy);/// 配置退出鉤子上訂閱的委託如何執行的策略。 , TRecipient, TParent> OnExitPolicy(TransitionPolicy policy);/// 確定此轉換進入哪個狀態。 (TState state);/// 確定轉移到目前狀態。於OnEntryPolicy(TransitionPolicy.ChildFirstWithCullingInclusive).OnExitPolicy(TransitionPolicy.ParentFirstWithCullingInclusive).Goto(currentState).///OrunParentActions 為false:當前狀態的表執行。會引發OnEntry 或OnExit 事件。 > : IGoto<TState>where TState : notnullwhere TEvent : notnull{/// 配置如何執行入口鉤子上訂閱的委託的策略。 TEvent, TRecipient, TParent> OnEntryPolicy(TransitionPolicy policy);/// 配置退出鉤子上訂閱的委託應如何執行的策略。 TEvent, TRecipient, TParent> OnExitPolicy(TransitionPolicy policy);/// 決定此轉換轉到哪個狀態public TParent Goto(TState state );/// 確定轉換到目前狀態。方法。的委託不應執行。父級上的訂閱委託(不包括)兩個狀態之間的最後一個公共父級。的最後一個公共父級。的訂閱委託首先運行,直到到達(包括)兩個狀態之間的最後一個公共父級。取得index處指定的元素。 }/// 取得切片的計數。 }/// 取得此切片的 <see cref="ReadOnlyMemory{T}"/>。 }/// 取得此切片的 <see cref="ReadOnlySpan{T}"/>。 }/// 取得切片的枚舉器。目前元素枚舉器的 public T Current { get; }/// 移到枚舉的下一個元素.public bool MoveNext();/// 重設枚舉.public void Reset();}}