【问题标题】:More .net approach for dynamic state machine更多动态状态机的.net方法
【发布时间】:2013-04-09 21:38:53
【问题描述】:

我写了一个简单的动态 FSM。 Dynamic 表示状态转换是动态的而不是静态的,如ConcreteStateB 所示。

namespace FSM_Example
{
    using System;

    class Program
    {
        static void Main()
        {
            var context = new Context(new ConcreteStateA());
            context.Run();
            Console.Read();
        }
    }

    abstract class State
    {
        public abstract void Execute(Context context);
    }

    class ConcreteStateA : State
    {
        public override void Execute(Context context)
        {
            context.State = new ConcreteStateB();
        }
    }

    class ConcreteStateB : State
    {
        public override void Execute(Context context)
        {
            Console.Write("Input state: ");
            string input = Console.ReadLine();
            context.State = input == "e" ? null : new ConcreteStateA();
        }
    }

    class Context
    {
        private State _state;

        public Context(State state)
        {
            State = state;
        }

        public State State
        {
            get { return _state; }
            set
            {
                _state = value;
                Console.WriteLine("State: " + _state.GetType().Name);
            }
        }

        public void Run()
        {
            while (_state != null)
            {
                _state.Execute(this);
            }
        }
    }
}

这实现了GoF305 中描述的状态机。

由于我是 C# 和 .net 的新手:是否有更好的方法使用来自 .netC# 的更具体的功能来实现这一目标?

【问题讨论】:

    标签: c# .net winforms design-patterns state-machine


    【解决方案1】:

    Outcoldman 的回答提供了许多不错的选择。

    现在,我知道根据模式,下面的代码不是正确的 FSM,但对于非常简单的实现,它可以帮助您避免编写大量额外的子类。这只是决定适合工作的工具的问题。这一篇主要围绕Action<T>泛型委托的使用:

    public class Context
    {
        public Action<Context> State { get; internal set; }
    
        public Context(Action<Context> state)
        {
            State = state;
        }
    
        public void Run()
        {
            while (State != null)
            {
                State(this);
            }
        }
    }
    

    并将“状态机”设为:

    public static class SimpleStateMachine
    {
        public static void StateA(Context context)
        {
            context.State = StateB;
        }
        public static void StateB(Context context)
        {
            Console.Write("Input state: ");
            var input = Console.ReadLine();
            context.State = input == "e" ? (Action<Context>)null : StateA;
        }
    }
    

    为了启动您将使用的流程:

    var context = new Context(SimpleStateMachine.StateA);
    context.Run();
    Console.Read();
    

    此外,对于不相关的状态,您也可以使用 Lambda 表达式,例如:

    Action<Context> process = context =>
        {
            //do something
            context.State = nextContext =>
                {
                    //something else
                    nextContext.State = null;
                };
        };
    

    【讨论】:

      【解决方案2】:

      您可以应用很多方法,但主要取决于您需要完成的任务。

      1. 您可以使用接口代替抽象类。在 C# 中,您不能继承多个类,因此最好不要从实现中采用此选项。

        interface IState
        {
             void Handle(Context context);
        }
        
      2. 您可以使用泛型,因此您可以为状态模式编写一次基接口/类并在任何地方使用它:

        abstract class IState<T>
        {
             void Handle(T context);
        }
        
      3. 接下来的事情取决于您要隐藏或不想隐藏的内容。例如,您可能可以隐藏属性状态的设置器,以确保没有人可以在您的 dll 之外使用,因此您可以将该属性的设置器设置为internal

      4. 您可以使用异步进行状态更改,例如

        interface IState
        {
             Task HandleAsync(Context context);
        }
        
        class Context
        {
            // ...
        
            public async Task RunAsync()
            {
                while (_state != null)
                {
                    await _state.HandleAsync(this);
                }
            }
        }
        
      5. 我敢打赌,有人已经用 Rx 实现了它

      【讨论】:

        猜你喜欢
        • 2019-07-23
        • 2011-04-18
        • 1970-01-01
        • 2013-01-25
        • 1970-01-01
        • 2010-10-12
        • 2019-12-30
        • 2022-09-23
        • 1970-01-01
        相关资源
        最近更新 更多