سبيل المثال آلة بسيطة في C#؟




مشاريع جاهزة بلغة c# (15)

I would recommend state.cs . I personally used state.js (the JavaScript version) and am very happy with it. That C# version works in a similar way.

You instantiate states:

        // create the state machine
        var player = new StateMachine<State>( "player" );

        // create some states
        var initial = player.CreatePseudoState( "initial", PseudoStateKind.Initial );
        var operational = player.CreateCompositeState( "operational" );
        ...

You instantiate some transitions:

        var t0 = player.CreateTransition( initial, operational );
        player.CreateTransition( history, stopped );
        player.CreateTransition<String>( stopped, running, ( state, command ) => command.Equals( "play" ) );
        player.CreateTransition<String>( active, stopped, ( state, command ) => command.Equals( "stop" ) );

You define actions on states and transitions:

    t0.Effect += DisengageHead;
    t0.Effect += StopMotor;

And that's (pretty much) it. Look at the website for more information.

تحديث:

شكرا مرة أخرى على الأمثلة ، فقد كانت مفيدة للغاية ومع ما يلي لا أقصد اتخاذ أي شيء بعيدا عنهم.

أليست الأمثلة المعطاة حاليا ، بقدر ما أفهمها وآلات الدولة ، فقط نصف ما نفهمه عادة من قبل آلة الدولة؟
بمعنى أن الأمثلة تغير الحالة ولكنها لا تمثل إلا بتغيير قيمة متغير (وتسمح بتغييرات مختلفة في القيمة في حالات مختلفة) ، في حين أن جهاز الحالة عادةً يجب أن يغيّر سلوكه أيضًا ، والسلوك ليس (فقط) في بمعنى السماح بتغييرات مختلفة في القيمة لمتغير اعتمادًا على الحالة ، ولكن بمعنى السماح بتنفيذ طرق مختلفة لحالات مختلفة.

أو هل لدي فكرة خاطئة عن أجهزة الدولة واستخدامها الشائع؟

تحياتي الحارة

السؤال الأصلي:

لقد وجدت هذا النقاش حول أجهزة الدولة ومجموعات الكتل الموسيقية في c # والأدوات لإنشاء أجهزة الحالة وما ليس لـ C # ، لذلك وجدت الكثير من الأشياء المجردة ولكن كمسؤول عن ذلك ، فإن كل هذا مثير للارتباك قليلاً.

لذلك سيكون من الرائع أن يقدم شخص ما مثالًا عن التعليمات البرمجية لمصدر C # يحقق جهازًا حكوميًا بسيطًا مع ما لا يقل عن 3،4 ولاية ، وذلك للحصول على جوهره.


لم أحاول تطبيق FSM في C # حتى الآن ، ولكن كل هذه الأصوات (أو تبدو) معقدة للغاية على الطريقة التي تعاملت بها FSM في الماضي بلغات منخفضة المستوى مثل C أو ASM.

أعتقد أن الطريقة التي أعرفها دائمًا تسمى شيئًا مثل "حلقة متكررة". في ذلك ، يكون لديك في الأساس حلقة "أثناء" تخرج دوريًا استنادًا إلى الأحداث (المقاطعات) ، ثم تعود إلى الحلقة الرئيسية مرة أخرى.

ضمن معالجات المقاطعة ، يمكنك تمرير CurrentState وإرجاع NextState ، ثم الكتابة فوق متغير CurrentState في الحلقة الرئيسية. أنت تفعل هذا الإعلان إلى ما لا نهاية حتى يغلق البرنامج (أو يعيد microcontroller).

ما أرى أن الإجابات الأخرى تبدو معقدة للغاية مقارنة بكيفية تنفيذ FSM ، في رأيي ، لتنفيذها ؛ يكمن جمالها في بساطتها ويمكن أن تكون FSM معقدة للغاية مع العديد من الحالات والتحولات ، ولكنها تسمح بعملية معقدة يمكن تفكيكها وهضمها بسهولة.

أدرك أنه لا ينبغي أن يتضمن جوابي سؤالاً آخر ، لكنني مجبر على السؤال: لماذا تبدو هذه الحلول المقترحة الأخرى معقدة للغاية؟
يبدو أنها أقرب إلى ضرب مسمار صغير بمطرقة ثقيلة عملاقة.


بعض الترويجي الذاتي المخزي هنا ، لكن منذ فترة وجيزة ، قمت بإنشاء مكتبة تسمى YieldMachine والتي تسمح YieldMachine آلة الحالة المحدودة التعقيد بطريقة نظيفة وبسيطة للغاية. على سبيل المثال ، ضع في اعتبارك مصباحًا:

لاحظ أن جهاز الحالة هذا يحتوي على مشغلين و 3 حالات. في قانون YieldMachine ، نكتب طريقة واحدة لكل السلوك المرتبط بالدولة ، حيث نرتكب الفظاعة المروعة باستخدام goto لكل ولاية. يصبح المشغل خاصية أو حقل من نوع Action ، مزين Trigger تسمى Trigger . لقد علقت مدونة الدولة الأولى وتحولاتها أدناه ؛ الدول التالية تتبع نفس النمط.

public class Lamp : StateMachine
{
    // Triggers (or events, or actions, whatever) that our
    // state machine understands.
    [Trigger]
    public readonly Action PressSwitch;

    [Trigger]
    public readonly Action GotError;

    // Actual state machine logic
    protected override IEnumerable WalkStates()
    {
    off:                                       
        Console.WriteLine("off.");
        yield return null;

        if (Trigger == PressSwitch) goto on;
        InvalidTrigger();

    on:
        Console.WriteLine("*shiiine!*");
        yield return null;

        if (Trigger == GotError) goto error;
        if (Trigger == PressSwitch) goto off;
        InvalidTrigger();

    error:
        Console.WriteLine("-err-");
        yield return null;

        if (Trigger == PressSwitch) goto off;
        InvalidTrigger();
    }
}

قصيرة وجميلة ، إيه!

يتم التحكم في آلة الحالة هذه ببساطة عن طريق إرسال المشغلات إليها:

var sm = new Lamp();
sm.PressSwitch(); //go on
sm.PressSwitch(); //go off

sm.PressSwitch(); //go on
sm.GotError();    //get error
sm.PressSwitch(); //go off

فقط للتوضيح ، لقد أضفت بعض التعليقات إلى الحالة الأولى لمساعدتك على فهم كيفية استخدام هذا.

    protected override IEnumerable WalkStates()
    {
    off:                                       // Each goto label is a state

        Console.WriteLine("off.");             // State entry actions

        yield return null;                     // This means "Wait until a 
                                               // trigger is called"

                                               // Ah, we got triggered! 
                                               //   perform state exit actions 
                                               //   (none, in this case)

        if (Trigger == PressSwitch) goto on;   // Transitions go here: 
                                               // depending on the trigger 
                                               // that was called, go to
                                               // the right state

        InvalidTrigger();                      // Throw exception on 
                                               // invalid trigger

        ...

هذا يعمل لأن المحول البرمجي C # بالفعل إنشاء جهاز حالة داخليًا لكل أسلوب يستخدم yield return . يستخدم هذا البناء عادةً لإنشاء تسلسل للبيانات بكسل ، لكننا في هذه الحالة لا نهتم فعلياً بالتسلسل المرتجع (وهو كل الأصفار على أية حال) ، ولكن في سلوك الدولة الذي يتم إنشاؤه تحت غطاء المحرك.

تقوم الطبقة الأساسية StateMachine ببعض التفكير في الإنشاء لتعيين التعليمة البرمجية لكل إجراء [Trigger] ، والذي يقوم بتعيين عضو Trigger ويقوم بنقل جهاز الحالة إلى الأمام.

ولكنك لا تحتاج حقًا لفهم الأجزاء الداخلية حتى تتمكن من استخدامها.


اليوم أنا في عمق نمط تصميم الدولة. فعلت واختبرت ThreadState ، والتي تساوي (+/-) إلى خيوط في C # ، كما هو موضح في الصورة من وصف رابط الدخول هنا

يمكنك بسهولة إضافة حالات جديدة ، وتكوين التحركات من حالة واحدة إلى أخرى من السهل جدا لأنها أغلقت في تنفيذ الدولة

تنفيذ واستخدام في: تنفذ NET ThreadState بواسطة نمط تصميم الدولة


لقد ساهمت بهذا للتو:

https://code.google.com/p/ysharp/source/browse/#svn%2Ftrunk%2FStateMachinesPoC

فيما يلي أحد الأمثلة على إعادة توجيه الأوامر المباشرة وغير المباشرة للأوامر ، مع الحالات كـ IObserver (للإشارة) ، وبالتالي المستجيبين لمصدر إشارة ، IObservable (للإشارة):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Test
{
    using Machines;

    public static class WatchingTvSampleAdvanced
    {
        // Enum type for the transition triggers (instead of System.String) :
        public enum TvOperation { Plug, SwitchOn, SwitchOff, Unplug, Dispose }

        // The state machine class type is also used as the type for its possible states constants :
        public class Television : NamedState<Television, TvOperation, DateTime>
        {
            // Declare all the possible states constants :
            public static readonly Television Unplugged = new Television("(Unplugged TV)");
            public static readonly Television Off = new Television("(TV Off)");
            public static readonly Television On = new Television("(TV On)");
            public static readonly Television Disposed = new Television("(Disposed TV)");

            // For convenience, enter the default start state when the parameterless constructor executes :
            public Television() : this(Television.Unplugged) { }

            // To create a state machine instance, with a given start state :
            private Television(Television value) : this(null, value) { }

            // To create a possible state constant :
            private Television(string moniker) : this(moniker, null) { }

            private Television(string moniker, Television value)
            {
                if (moniker == null)
                {
                    // Build the state graph programmatically
                    // (instead of declaratively via custom attributes) :
                    Handler<Television, TvOperation, DateTime> stateChangeHandler = StateChange;
                    Build
                    (
                        new[]
                        {
                            new { From = Television.Unplugged, When = TvOperation.Plug, Goto = Television.Off, With = stateChangeHandler },
                            new { From = Television.Unplugged, When = TvOperation.Dispose, Goto = Television.Disposed, With = stateChangeHandler },
                            new { From = Television.Off, When = TvOperation.SwitchOn, Goto = Television.On, With = stateChangeHandler },
                            new { From = Television.Off, When = TvOperation.Unplug, Goto = Television.Unplugged, With = stateChangeHandler },
                            new { From = Television.Off, When = TvOperation.Dispose, Goto = Television.Disposed, With = stateChangeHandler },
                            new { From = Television.On, When = TvOperation.SwitchOff, Goto = Television.Off, With = stateChangeHandler },
                            new { From = Television.On, When = TvOperation.Unplug, Goto = Television.Unplugged, With = stateChangeHandler },
                            new { From = Television.On, When = TvOperation.Dispose, Goto = Television.Disposed, With = stateChangeHandler }
                        },
                        false
                    );
                }
                else
                    // Name the state constant :
                    Moniker = moniker;
                Start(value ?? this);
            }

            // Because the states' value domain is a reference type, disallow the null value for any start state value : 
            protected override void OnStart(Television value)
            {
                if (value == null)
                    throw new ArgumentNullException("value", "cannot be null");
            }

            // When reaching a final state, unsubscribe from all the signal source(s), if any :
            protected override void OnComplete(bool stateComplete)
            {
                // Holds during all transitions into a final state
                // (i.e., stateComplete implies IsFinal) :
                System.Diagnostics.Debug.Assert(!stateComplete || IsFinal);

                if (stateComplete)
                    UnsubscribeFromAll();
            }

            // Executed before and after every state transition :
            private void StateChange(IState<Television> state, ExecutionStep step, Television value, TvOperation info, DateTime args)
            {
                // Holds during all possible transitions defined in the state graph
                // (i.e., (step equals ExecutionStep.LeaveState) implies (not state.IsFinal))
                System.Diagnostics.Debug.Assert((step != ExecutionStep.LeaveState) || !state.IsFinal);

                // Holds in instance (i.e., non-static) transition handlers like this one :
                System.Diagnostics.Debug.Assert(this == state);

                switch (step)
                {
                    case ExecutionStep.LeaveState:
                        var timeStamp = ((args != default(DateTime)) ? String.Format("\t\t(@ {0})", args) : String.Empty);
                        Console.WriteLine();
                        // 'value' is the state value that we are transitioning TO :
                        Console.WriteLine("\tLeave :\t{0} -- {1} -> {2}{3}", this, info, value, timeStamp);
                        break;
                    case ExecutionStep.EnterState:
                        // 'value' is the state value that we have transitioned FROM :
                        Console.WriteLine("\tEnter :\t{0} -- {1} -> {2}", value, info, this);
                        break;
                    default:
                        break;
                }
            }

            public override string ToString() { return (IsConstant ? Moniker : Value.ToString()); }
        }

        public static void Run()
        {
            Console.Clear();

            // Create a signal source instance (here, a.k.a. "remote control") that implements
            // IObservable<TvOperation> and IObservable<KeyValuePair<TvOperation, DateTime>> :
            var remote = new SignalSource<TvOperation, DateTime>();

            // Create a television state machine instance (automatically set in a default start state),
            // and make it subscribe to a compatible signal source, such as the remote control, precisely :
            var tv = new Television().Using(remote);
            bool done;

            // Always holds, assuming the call to Using(...) didn't throw an exception (in case of subscription failure) :
            System.Diagnostics.Debug.Assert(tv != null, "There's a bug somewhere: this message should never be displayed!");

            // As commonly done, we can trigger a transition directly on the state machine :
            tv.MoveNext(TvOperation.Plug, DateTime.Now);

            // Alternatively, we can also trigger transitions by emitting from the signal source / remote control
            // that the state machine subscribed to / is an observer of :
            remote.Emit(TvOperation.SwitchOn, DateTime.Now);
            remote.Emit(TvOperation.SwitchOff);
            remote.Emit(TvOperation.SwitchOn);
            remote.Emit(TvOperation.SwitchOff, DateTime.Now);

            done =
                (
                    tv.
                        MoveNext(TvOperation.Unplug).
                        MoveNext(TvOperation.Dispose) // MoveNext(...) returns null iff tv.IsFinal == true
                    == null
                );

            remote.Emit(TvOperation.Unplug); // Ignored by the state machine thanks to the OnComplete(...) override above

            Console.WriteLine();
            Console.WriteLine("Is the TV's state '{0}' a final state? {1}", tv.Value, done);

            Console.WriteLine();
            Console.WriteLine("Press any key...");
            Console.ReadKey();
        }
    }
}

ملاحظة: هذا المثال اصطناعيًا ويهدف في الغالب إلى عرض عدد من الميزات المتعامدة. يجب أن يكون هناك حاجة حقيقية إلى تنفيذ مجال قيمة الدولة نفسها من خلال فئة كاملة في مهب ، وذلك باستخدام CRTP (انظر: http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern ) مثل هذا.

هنا لحالة استخدام تنفيذ أكثر بساطة وأرجح بكثير (باستخدام نوع التعداد البسيط كمجال قيمة الدول) ، لنفس جهاز الحالة ، ومع نفس حالة الاختبار:

https://code.google.com/p/ysharp/source/browse/trunk/StateMachinesPoC/WatchingTVSample.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Test
{
    using Machines;

    public static class WatchingTvSample
    {
        public enum Status { Unplugged, Off, On, Disposed }

        public class DeviceTransitionAttribute : TransitionAttribute
        {
            public Status From { get; set; }
            public string When { get; set; }
            public Status Goto { get; set; }
            public object With { get; set; }
        }

        // State<Status> is a shortcut for / derived from State<Status, string>,
        // which in turn is a shortcut for / derived from State<Status, string, object> :
        public class Device : State<Status>
        {
            // Executed before and after every state transition :
            protected override void OnChange(ExecutionStep step, Status value, string info, object args)
            {
                if (step == ExecutionStep.EnterState)
                {
                    // 'value' is the state value that we have transitioned FROM :
                    Console.WriteLine("\t{0} -- {1} -> {2}", value, info, this);
                }
            }

            public override string ToString() { return Value.ToString(); }
        }

        // Since 'Device' has no state graph of its own, define one for derived 'Television' :
        [DeviceTransition(From = Status.Unplugged, When = "Plug", Goto = Status.Off)]
        [DeviceTransition(From = Status.Unplugged, When = "Dispose", Goto = Status.Disposed)]
        [DeviceTransition(From = Status.Off, When = "Switch On", Goto = Status.On)]
        [DeviceTransition(From = Status.Off, When = "Unplug", Goto = Status.Unplugged)]
        [DeviceTransition(From = Status.Off, When = "Dispose", Goto = Status.Disposed)]
        [DeviceTransition(From = Status.On, When = "Switch Off", Goto = Status.Off)]
        [DeviceTransition(From = Status.On, When = "Unplug", Goto = Status.Unplugged)]
        [DeviceTransition(From = Status.On, When = "Dispose", Goto = Status.Disposed)]
        public class Television : Device { }

        public static void Run()
        {
            Console.Clear();

            // Create a television state machine instance, and return it, set in some start state :
            var tv = new Television().Start(Status.Unplugged);
            bool done;

            // Holds iff the chosen start state isn't a final state :
            System.Diagnostics.Debug.Assert(tv != null, "The chosen start state is a final state!");

            // Trigger some state transitions with no arguments
            // ('args' is ignored by this state machine's OnChange(...), anyway) :
            done =
                (
                    tv.
                        MoveNext("Plug").
                        MoveNext("Switch On").
                        MoveNext("Switch Off").
                        MoveNext("Switch On").
                        MoveNext("Switch Off").
                        MoveNext("Unplug").
                        MoveNext("Dispose") // MoveNext(...) returns null iff tv.IsFinal == true
                    == null
                );

            Console.WriteLine();
            Console.WriteLine("Is the TV's state '{0}' a final state? {1}", tv.Value, done);

            Console.WriteLine();
            Console.WriteLine("Press any key...");
            Console.ReadKey();
        }
    }
}

'HTH



قد ترغب في استخدام واحدة من أجهزة الدولة الحالية المفتوحة المصدر. على سبيل المثال ، تم العثور على bbv.Common.StateMachine على http://code.google.com/p/bbvcommon/wiki/StateMachine . يحتوي على بنية بديهية غاية في السهولة والكثير من الميزات مثل ، إجراءات الدخول / الخروج ، إجراءات النقل ، الحراس ، التراتبية ، التنفيذ السلبي (تم تنفيذها على مؤشر المتصل) والتنفيذ النشط (الخيط الخاص الذي يعمل عليه fsm ، تتم إضافة الأحداث إلى قائمة انتظار).

مع أخذ Juliets على سبيل المثال ، يصبح تعريف الآلة الحكومية أمرًا سهلاً للغاية:

var fsm = new PassiveStateMachine<ProcessState, Command>();
fsm.In(ProcessState.Inactive)
   .On(Command.Exit).Goto(ProcessState.Terminated).Execute(SomeTransitionAction)
   .On(Command.Begin).Goto(ProcessState.Active);
fsm.In(ProcessState.Active)
   .ExecuteOnEntry(SomeEntryAction)
   .ExecuteOnExit(SomeExitAction)
   .On(Command.End).Goto(ProcessState.Inactive)
   .On(Command.Pause).Goto(ProcessState.Paused);
fsm.In(ProcessState.Paused)
   .On(Command.End).Goto(ProcessState.Inactive).OnlyIf(SomeGuard)
   .On(Command.Resume).Goto(ProcessState.Active);
fsm.Initialize(ProcessState.Inactive);
fsm.Start();

fsm.Fire(Command.Begin);

تحديث : انتقل موقع المشروع إلى: https://github.com/appccelerate/statemachine


FiniteStateMachine is a Simple State Machine, written in C# Link

Advantages tu use my library FiniteStateMachine:

  1. Define a "context" class to present a single interface to the outside world.
  2. Define a State abstract base class.
  3. Represent the different "states" of the state machine as derived classes of the State base class.
  4. Define state-specific behavior in the appropriate State derived classes.
  5. Maintain a pointer to the current "state" in the "context" class.
  6. To change the state of the state machine, change the current "state" pointer.

Download DLL Download

Example on LINQPad:

void Main()
{
            var machine = new SFM.Machine(new StatePaused());
            var output = machine.Command("Input_Start", Command.Start);
            Console.WriteLine(Command.Start.ToString() + "->  State: " + machine.Current);
            Console.WriteLine(output);

            output = machine.Command("Input_Pause", Command.Pause);
            Console.WriteLine(Command.Pause.ToString() + "->  State: " + machine.Current);
            Console.WriteLine(output);
            Console.WriteLine("-------------------------------------------------");
}
    public enum Command
    {
        Start,
        Pause,
    }

    public class StateActive : SFM.State
    {

        public override void Handle(SFM.IContext context)

        {
            //Gestione parametri
            var input = (String)context.Input;
            context.Output = input;

            //Gestione Navigazione
            if ((Command)context.Command == Command.Pause) context.Next = new StatePaused();
            if ((Command)context.Command == Command.Start) context.Next = this;

        }
    }


public class StatePaused : SFM.State
{

     public override void Handle(SFM.IContext context)

     {

         //Gestione parametri
         var input = (String)context.Input;
         context.Output = input;

         //Gestione Navigazione
         if ((Command)context.Command == Command.Start) context.Next = new  StateActive();
         if ((Command)context.Command == Command.Pause) context.Next = this;


     }

 }

هيريس مثال على آلة الحالة المحدودة المتناهية في الصغر ، والتي تمثل جهازًا إلكترونيًا مبسطًا جدًا (مثل جهاز التلفزيون)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace fsm
{
class Program
{
    static void Main(string[] args)
    {
        var fsm = new FiniteStateMachine();
        Console.WriteLine(fsm.State);
        fsm.ProcessEvent(FiniteStateMachine.Events.PlugIn);
        Console.WriteLine(fsm.State);
        fsm.ProcessEvent(FiniteStateMachine.Events.TurnOn);
        Console.WriteLine(fsm.State);
        fsm.ProcessEvent(FiniteStateMachine.Events.TurnOff);
        Console.WriteLine(fsm.State);
        fsm.ProcessEvent(FiniteStateMachine.Events.TurnOn);
        Console.WriteLine(fsm.State);
        fsm.ProcessEvent(FiniteStateMachine.Events.RemovePower);
        Console.WriteLine(fsm.State);
        Console.ReadKey();
    }

    class FiniteStateMachine
    {
        public enum States { Start, Standby, On };
        public States State { get; set; }

        public enum Events { PlugIn, TurnOn, TurnOff, RemovePower };

        private Action[,] fsm;

        public FiniteStateMachine()
        {
            this.fsm = new Action[3, 4] { 
                //PlugIn,       TurnOn,                 TurnOff,            RemovePower
                {this.PowerOn,  null,                   null,               null},              //start
                {null,          this.StandbyWhenOff,    null,               this.PowerOff},     //standby
                {null,          null,                   this.StandbyWhenOn, this.PowerOff} };   //on
        }
        public void ProcessEvent(Events theEvent)
        {
            this.fsm[(int)this.State, (int)theEvent].Invoke();
        }

        private void PowerOn() { this.State = States.Standby; }
        private void PowerOff() { this.State = States.Start; }
        private void StandbyWhenOn() { this.State = States.Standby; }
        private void StandbyWhenOff() { this.State = States.On; }
    }
}
}

In my opinion a state machine is not only meant for changing states but also (very important) for handling triggers/events within a specific state. If you want to understand state machine design pattern better, a good description can be found within the book Head First Design Patterns, page 320 .

It is not only about the states within variables but also about handling triggers within the different states. Great chapter (and no, there is no fee for me in mentioning this :-) which contains just an easy to understand explanation.


من المفيد أن تتذكر أن أجهزة الحالة هي تجريد ، ولا تحتاج إلى أدوات معينة لإنشاء واحدة ، إلا أن الأدوات قد تكون مفيدة.

يمكنك على سبيل المثال تحقيق جهاز الحالة مع وظائف:

void Hunt(IList<Gull> gulls)
{
    if (gulls.Empty())
       return;

    var target = gulls.First();
    TargetAcquired(target, gulls);
}

void TargetAcquired(Gull target, IList<Gull> gulls)
{
    var balloon = new WaterBalloon(weightKg: 20);

    this.Cannon.Fire(balloon);

    if (balloon.Hit)
    {
       TargetHit(target, gulls);
    }
    else
       TargetMissed(target, gulls);
}

void TargetHit(Gull target, IList<Gull> gulls)
{
    Console.WriteLine("Suck on it {0}!", target.Name);
    Hunt(gulls);
}

void TargetMissed(Gull target, IList<Gull> gulls)
{
    Console.WriteLine("I'll get ya!");
    TargetAcquired(target, gulls);
}

ستقوم هذه الآلة بمطاردة النوارس ومحاولة ضربهم بالونات الماء. إذا فاتته فإنه سيحاول إطلاق واحدة حتى يضرب (يمكن أن تفعل مع بعض التوقعات الواقعية ؛)) ، وإلا فإنه سوف ينثر في وحدة التحكم. تواصل الصيد حتى يتم التخلص من النوارس.

كل وظيفة تتوافق مع كل حالة ؛ لا تظهر حالات البداية والنهاية (أو القبول ). هناك على الارجح المزيد من الدول هناك من طراز من خلال وظائف على الرغم من. على سبيل المثال بعد إطلاق البالون ، تكون الآلة بالفعل في حالة أخرى مما كانت عليه قبل ذلك ، لكنني قررت أن هذا التمييز غير عملي.

الطريقة الشائعة هي استخدام الفئات لتمثيل الحالات ، ثم توصيلها بطرق مختلفة.


يمكنك ترميز كتلة مكرر تتيح لك تنفيذ كتلة تعليمات برمجية بطريقة منسقة. إن طريقة فك شفرة الكود لا يجب أن تتوافق مع أي شيء ، إنها الطريقة التي تريد أن ترمز بها. فمثلا:

IEnumerable<int> CountToTen()
{
    System.Console.WriteLine("1");
    yield return 0;
    System.Console.WriteLine("2");
    System.Console.WriteLine("3");
    System.Console.WriteLine("4");
    yield return 0;
    System.Console.WriteLine("5");
    System.Console.WriteLine("6");
    System.Console.WriteLine("7");
    yield return 0;
    System.Console.WriteLine("8");
    yield return 0;
    System.Console.WriteLine("9");
    System.Console.WriteLine("10");
}

في هذه الحالة ، عند استدعاء CountToTen ، لا يتم تنفيذ أي شيء فعليًا حتى الآن. ما تحصل عليه هو بشكل فعال مولد آلة الدولة ، والتي يمكنك إنشاء مثيل جديد لجهاز الدولة. يمكنك القيام بذلك عن طريق استدعاء GetEnumerator (). يكون IEnumerator الناتج بشكل فعال على جهاز حالة يمكنك القيادة عن طريق استدعاء MoveNext (...).

وهكذا ، في هذا المثال ، في المرة الأولى التي تقوم فيها باستدعاء MoveNext (...) سترى "1" مكتوبة إلى وحدة التحكم ، وفي المرة التالية التي تقوم فيها بالاتصال بـ MoveNext (...) سترى 2 و 3 و 4 و ثم 5 ، 6 ، 7 ثم 8 ، ثم 9 ، 10. كما ترى ، إنها آلية مفيدة لتدبير كيفية حدوث الأشياء.


لنبدأ بهذا الشكل البسيط للحالة:

لدينا:

  • 4 حالات (غير نشط ، نشط ، متوقف مؤقتًا ، ومغلق)
  • 5 أنواع من التحولات الولائية (Begin Command، End Command، Pause Command، Resume Command، Exit Command).

يمكنك تحويل هذا إلى C # في عدد قليل من الطرق ، مثل تنفيذ عبارة switch في الحالة الحالية والأمر ، أو البحث عن انتقالات في جدول النقل. بالنسبة لجهاز الحالة البسيطة هذا ، أفضّل جدول انتقال ، والذي يسهل تمثيله باستخدام Dictionary :

using System;
using System.Collections.Generic;

namespace Juliet
{
    public enum ProcessState
    {
        Inactive,
        Active,
        Paused,
        Terminated
    }

    public enum Command
    {
        Begin,
        End,
        Pause,
        Resume,
        Exit
    }

    public class Process
    {
        class StateTransition
        {
            readonly ProcessState CurrentState;
            readonly Command Command;

            public StateTransition(ProcessState currentState, Command command)
            {
                CurrentState = currentState;
                Command = command;
            }

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

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

        Dictionary<StateTransition, ProcessState> transitions;
        public ProcessState CurrentState { get; private set; }

        public Process()
        {
            CurrentState = ProcessState.Inactive;
            transitions = new Dictionary<StateTransition, ProcessState>
            {
                { new StateTransition(ProcessState.Inactive, Command.Exit), ProcessState.Terminated },
                { new StateTransition(ProcessState.Inactive, Command.Begin), ProcessState.Active },
                { new StateTransition(ProcessState.Active, Command.End), ProcessState.Inactive },
                { new StateTransition(ProcessState.Active, Command.Pause), ProcessState.Paused },
                { new StateTransition(ProcessState.Paused, Command.End), ProcessState.Inactive },
                { new StateTransition(ProcessState.Paused, Command.Resume), ProcessState.Active }
            };
        }

        public ProcessState GetNext(Command command)
        {
            StateTransition transition = new StateTransition(CurrentState, command);
            ProcessState nextState;
            if (!transitions.TryGetValue(transition, out nextState))
                throw new Exception("Invalid transition: " + CurrentState + " -> " + command);
            return nextState;
        }

        public ProcessState MoveNext(Command command)
        {
            CurrentState = GetNext(command);
            return CurrentState;
        }
    }


    public class Program
    {
        static void Main(string[] args)
        {
            Process p = new Process();
            Console.WriteLine("Current State = " + p.CurrentState);
            Console.WriteLine("Command.Begin: Current State = " + p.MoveNext(Command.Begin));
            Console.WriteLine("Command.Pause: Current State = " + p.MoveNext(Command.Pause));
            Console.WriteLine("Command.End: Current State = " + p.MoveNext(Command.End));
            Console.WriteLine("Command.Exit: Current State = " + p.MoveNext(Command.Exit));
            Console.ReadLine();
        }
    }
}

كمسألة تفضيل شخصي ، أرغب في تصميم أجهزة الحالة الخاصة بي باستخدام دالة GetNext لإرجاع الحالة التالية بشكل deterministically ، ووظيفة MoveNext لتحول جهاز الحالة.


ايم نشر annother الجواب هنا لأن هذا هو آلات الدولة من منظور مختلف. بصري جدا.

اجابتي الاصلية هي كود غير عادي clasic. أعتقد أن بصري تماما كما رمز يذهب لأنه من الصفيف الذي يجعل تصور جهاز الدولة بسيطة. الجانب السلبي هو أن تكتب كل هذا. تخفف إجابة Remos من مجهود كتابة رمز الغلاية ، ولكنها أقل بروزًا. هناك البديل الثالث. حقا رسم آلة الدولة.

إذا كنت تستخدم .NET ويمكن أن تستهدف الإصدار 4 من وقت التشغيل ، فحينئذٍ يكون لديك خيار استخدام أنشطة جهاز حالة سير العمل . هذه في جوهرها تمكنك من رسم جهاز الدولة (كما هو الحال في الرسم البياني لجولييت) وجعل وقت التشغيل WF ينفذها لك.

راجع مقالة MSDN Building State Machines مع Windows Workflow Foundation لمزيد من التفاصيل ، وهذا الموقع CodePlex للحصول على أحدث إصدار.

هذا هو الخيار الذي أفضل دائمًا عند استهداف .NET لأنه يسهل رؤيته وتغييره وشرحه لغير المبرمجين ؛ الصور تستحق ألف كلمة كما يقولون!


وجدت هذا البرنامج التعليمي العظيم على الانترنت وساعدني على التفاف رأسي حول آلات الحالة المحدودة.

http://gamedevelopment.tutsplus.com/tutorials/finite-state-machines-theory-and-implementation--gamedev-11867

البرنامج التعليمي هو ملحد اللغة ، لذلك يمكن تكييفها بسهولة لاحتياجات C # الخاص بك.

أيضا ، من السهل فهم المثال المستخدم (النملة التي تبحث عن الطعام).


من البرنامج التعليمي:

public class FSM {
    private var activeState :Function; // points to the currently active state function

    public function FSM() {
    }

    public function setState(state :Function) :void {
        activeState = state;
    }

    public function update() :void {
        if (activeState != null) {
            activeState();
        }
    }
}


public class Ant
{
    public var position   :Vector3D;
    public var velocity   :Vector3D;
    public var brain      :FSM;

    public function Ant(posX :Number, posY :Number) {
        position    = new Vector3D(posX, posY);
        velocity    = new Vector3D( -1, -1);
        brain       = new FSM();

        // Tell the brain to start looking for the leaf.
        brain.setState(findLeaf);
    }

    /**
    * The "findLeaf" state.
    * It makes the ant move towards the leaf.
    */
    public function findLeaf() :void {
        // Move the ant towards the leaf.
        velocity = new Vector3D(Game.instance.leaf.x - position.x, Game.instance.leaf.y - position.y);

        if (distance(Game.instance.leaf, this) <= 10) {
            // The ant is extremelly close to the leaf, it's time
            // to go home.
            brain.setState(goHome);
        }

        if (distance(Game.mouse, this) <= MOUSE_THREAT_RADIUS) {
            // Mouse cursor is threatening us. Let's run away!
            // It will make the brain start calling runAway() from
            // now on.
            brain.setState(runAway);
        }
    }

    /**
    * The "goHome" state.
    * It makes the ant move towards its home.
    */
    public function goHome() :void {
        // Move the ant towards home
        velocity = new Vector3D(Game.instance.home.x - position.x, Game.instance.home.y - position.y);

        if (distance(Game.instance.home, this) <= 10) {
            // The ant is home, let's find the leaf again.
            brain.setState(findLeaf);
        }
    }

    /**
    * The "runAway" state.
    * It makes the ant run away from the mouse cursor.
    */
    public function runAway() :void {
        // Move the ant away from the mouse cursor
        velocity = new Vector3D(position.x - Game.mouse.x, position.y - Game.mouse.y);

        // Is the mouse cursor still close?
        if (distance(Game.mouse, this) > MOUSE_THREAT_RADIUS) {
            // No, the mouse cursor has gone away. Let's go back looking for the leaf.
            brain.setState(findLeaf);
        }
    }

    public function update():void {
        // Update the FSM controlling the "brain". It will invoke the currently
        // active state function: findLeaf(), goHome() or runAway().
        brain.update();

        // Apply the velocity vector to the position, making the ant move.
        moveBasedOnVelocity();
    }

    (...)
}




state-machines