更新:

再次感谢你的例子,它们对我很有帮助,我并不是说 夺走他们的一切。

Aren't the currently given examples, as far as I understand them & state-machines, only half of what we usually understand by a state-machine? In the sense that the examples do change state but that's only represented by changing the value of a variable (and allowing different value- changes in different states), while usually, a state machine should also change its behavior, and behavior not (only) in the sense of allowing different value changes for a variable depending on the state, but in the sense of allowing different methods to be executed for different states.

还是我对状态机及其常用用法有误解?


最初的问题:

我发现了关于c#中的状态机和迭代器块的讨论,以及用于创建状态机和c#的工具,所以我发现了很多抽象的东西,但作为一个新手,所有这些都有点令人困惑。

因此,如果有人能提供一个c#源代码-示例,实现一个简单的状态机,可能只有3,4个状态,那就太好了,只是为了了解它的要点。


当前回答

下面是一个非常经典的有限状态机的例子,它模拟了一个非常简化的电子设备(比如电视)

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; }
    }
}
}

其他回答

这里有些无耻的自我宣传,但在不久前,我创建了一个名为YieldMachine的库,它允许以非常干净和简单的方式描述一个有限复杂性的状态机。例如,考虑一盏灯:

注意,这个状态机有2个触发器和3个状态。在YieldMachine代码中,我们为所有与状态相关的行为编写了一个方法,在这个方法中,我们对每个状态都使用goto,这是一种可怕的暴行。触发器变成Action类型的属性或字段,用一个称为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的方法创建了一个状态机。这个构造通常用于惰性地创建数据序列,但在这种情况下,我们实际上对返回的序列并不感兴趣(反正都是null),而是对在底层创建的状态行为感兴趣。

StateMachine基类对构造进行一些反射,将代码分配给每个[Trigger]操作,该操作设置Trigger成员并向前移动状态机。

但是你不需要真正理解它的内部原理就能使用它。

我在这里发布了另一个答案,因为这是从不同的角度来看状态机;非常视觉。

我最初的答案是经典的命令式代码。我认为它在代码运行时非常直观,因为数组使得状态机的可视化变得简单。缺点是你必须把这些都写下来。Remos的回答减轻了编写样板代码的工作量,但远没有那么直观。还有第三种选择;画状态机。

如果您正在使用。net,并且可以将运行时版本4作为目标,那么您可以选择使用工作流的状态机活动。从本质上讲,它们允许您绘制状态机(就像Juliet的图一样),并让WF运行时为您执行它。

有关更多细节,请参阅MSDN文章使用Windows Workflow Foundation构建状态机,以及最新版本的CodePlex站点。

这是我在瞄准。net时更喜欢的选项,因为它很容易看到、更改和向非程序员解释;俗话说,图片胜过千言万语!

关于stateppattern。这符合你的需要吗?

我认为这与上下文有关,但值得一试。

http://en.wikipedia.org/wiki/State_pattern

这让你的状态决定去哪里,而不是“对象”类。

布鲁诺

在网上找到了这个很棒的教程,它帮助我理解了有限状态机。

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();
    }

    (...)
}

今天我将深入学习状态设计模式。 我做了并测试了ThreadState,它等于c#中的线程(+/-),如图所示

你可以很容易地添加新的状态,配置从一个状态移动到另一个状态是非常容易的,因为它封装在状态实现中

实现和使用at:实现。net ThreadState by State设计模式