更新:

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

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个状态,那就太好了,只是为了了解它的要点。


当前回答

您可能希望使用现有的开源有限状态机之一。例如,在http://code.google.com/p/bbvcommon/wiki/StateMachine找到的bbv.Common.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

其他回答

您可以编写一个迭代器块,使您能够以编排的方式执行代码块。代码块是如何分解的并不一定要对应于任何东西,这只是你想要如何编码它。例如:

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。正如您所看到的,这是一种编排事情应该如何发生的有用机制。

在我看来,状态机不仅意味着改变状态,而且(非常重要)还意味着处理特定状态中的触发器/事件。如果您想更好地理解状态机设计模式,可以在《头部优先设计模式》一书的第320页中找到一个很好的描述。

它不仅涉及变量中的状态,还涉及处理不同状态中的触发器。很棒的一章(不,提到这个对我来说是免费的:-),它包含了一个简单易懂的解释。

不确定我是否错过了重点,但我认为这里没有一个答案是“简单的”状态机。我通常所说的简单状态机是使用一个内部有开关的循环。这就是我们在PLC /微芯片编程或C/ c++编程中使用的方法。

优点:

容易写。不需要特殊的物品和东西。你甚至不需要面向对象。 当它很小的时候,很容易理解。

缺点:

当有很多状态时,可能会变得相当大,很难阅读。

它是这样的:

public enum State
{
    First,
    Second,
    Third,
}

static void Main(string[] args)
{
    var state = State.First;
    // x and i are just examples for stuff that you could change inside the state and use for state transitions
    var x     = 0; 
    var i     = 0;

    // does not have to be a while loop. you could loop over the characters of a string too
    while (true)  
    {
        switch (state)
        {
            case State.First:
                // Do sth here
                if (x == 2)
                    state = State.Second;  
                    // you may or may not add a break; right after setting the next state
                // or do sth here
                if (i == 3)
                    state = State.Third;
                // or here
                break;
            case State.Second:
                // Do sth here
                if (x == 10)
                    state = State.First;
                // or do sth here
                break;
            case State.Third:
                // Do sth here
                if (x == 10)
                    state = State.First;
                // or do sth here
                break;
            default:
                // you may wanna throw an exception here.
                break;
        }
    }
}

如果它真的应该是一个状态机,你调用的方法会根据你所处的状态做出不同的反应:状态设计模式是更好的方法

我已经构建了一个Nuget库,它实现了一个简单而强大的状态机,并可在DI中注入。你可以从这里检查Nuget - State Machine

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

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

    (...)
}