Update:
再次感谢这些例子,他们非常有帮助,以下我并不是要从他们那里拿走任何东西 .
就我理解它们和状态机而言,目前给出的例子不是我们通常理解的状态机的一半吗?
在某种意义上,示例确实改变了状态,但是行为不是(仅)在允许变量取决于状态的情况下对变量进行不同的值更改,而是允许为不同的状态执行不同的方法 .
或者我对状态机及其常见用途存在误解?
最好的祝福
Original question:
我发现这个关于state machines & iterator blocks in c#的讨论和创建状态机的工具以及C#没有的东西,所以我发现了很多抽象的东西,但作为一个菜鸟所有这一切都有点令人困惑 .
因此,如果有人能够提供一个C#源代码示例,它可以实现一个简单的状态机,可能有3,4个状态,只是为了得到它的要点 .
18 回答
FiniteStateMachine是一个简单的状态机,用C#Link编写
优点tu使用我的库FiniteStateMachine:
定义"context"类以向外界呈现单个接口 .
定义状态抽象基类 .
将状态机的不同"states"表示为State基类的派生类 .
在适当的State派生类中定义特定于州的行为 .
保持指向"context"类中当前"state"的指针 .
要更改状态机的状态,请更改当前的"state"指针 .
下载DLL Download
LINQPad上的示例:
让我们从这个简单的状态图开始:
我们有:
4个州(非活动,活动,暂停和退出)
5种类型的状态转换(开始命令,结束命令,暂停命令,恢复命令,退出命令) .
您可以通过多种方式将其转换为C#,例如在当前状态和命令上执行switch语句,或在转换表中查找转换 . 对于这个简单的状态机,我更喜欢转换表,使用
Dictionary
很容易表示:作为个人喜好,我喜欢使用
GetNext
函数设计我的状态机以返回下一个状态deterministically,并使用MoveNext
函数来改变状态机 .您可能希望使用现有的开源有限状态机之一 . 例如 . bbv.Common.StateMachine在http://code.google.com/p/bbvcommon/wiki/StateMachine找到 . 它具有非常直观的流畅语法和许多功能,例如进入/退出操作,转换操作,警卫,分层,被动实现(在调用者的线程上执行)和活动实现(fsm运行的自己的线程,事件被添加到队列中) .
以Juliets为例,状态机的定义变得非常简单:
Update :项目位置已移至:https://github.com/appccelerate/statemachine
这是一个非常经典的有限状态机的例子,建模一个非常简化的电子设备(如电视)
这里有一些无耻的自我宣传,但不久前我创建了一个名为YieldMachine的库,它允许以非常简洁的方式描述有限复杂度的状态机 . 例如,考虑一盏灯:
请注意,此状态机有2个触发器和3个状态 . 在YieldMachine代码中,我们为所有与状态相关的行为编写了一个方法,其中我们为每个状态提交了使用
goto
的可怕的暴行 . 触发器成为Action
类型的属性或字段,使用名为Trigger
的属性进行修饰 . 我已经评论了第一个状态的代码及其转换如下;接下来的状态遵循相同的模式 .简短又好看,呃!
只需向其发送触发器即可控制此状态机:
为了澄清,我在第一个州添加了一些注释,以帮助您了解如何使用它 .
这是有效的,因为C#编译器实际上为每个使用
yield return
的方法在内部创建了一个状态机 . 这个构造通常用于懒惰地创建数据序列,但在这种情况下,我们实际上并不对返回的序列感兴趣(无论如何都是null),而是在引擎盖下创建的状态行为中 .StateMachine
基类对构造做了一些反思,为每个[Trigger]
动作分配代码,该动作设置Trigger
成员并向前移动状态机 .但是你真的不需要理解内部能够使用它 .
您可以编写迭代器块代码,以便以协调的方式执行代码块 . 代码块如何分解实际上不必与任何东西相对应,它只是你想要编码它的方式 . 例如:
在这种情况下,当您调用CountToTen时,实际上没有执行任何操作 . 你得到的实际上是一个状态机生成器,你可以为它创建一个状态机的新实例 . 您可以通过调用GetEnumerator()来完成此操作 . 生成的IEnumerator实际上是一个状态机,您可以通过调用MoveNext(...)来驱动它 .
因此,在这个例子中,第一次调用MoveNext(...)时,您会看到“1”写入控制台,下次调用MoveNext(...)时,您将看到2,3,4和然后是5,6,7和8,然后是9,10 . 正如你所看到的,它是一种有用的机制,用于协调事情应该如何发生 .
我在这里发布了另一个答案,因为这是来自不同角度的状态机;非常直观 .
我的原始答案是clasic不完整的代码 . 我认为它的代码非常直观,因为数组使得状态机的可视化变得简单 . 缺点是你必须写下这一切 . Remos的答案缓解了编写样板代码的努力,但远没有那么直观 . 还有第三种选择;真的画国家机器 .
如果您使用的是.NET并且可以定位运行时的第4版,那么您可以选择使用 workflow's state machine activities . 这些实质上是让你绘制状态机(就像在Juliet的图中一样)并让WF运行时为你执行它 .
有关更多详细信息,请参阅MSDN文章Building State Machines with Windows Workflow Foundation,有关最新版本,请参阅this CodePlex site .
这是我在定位.NET时总是喜欢的选项,因为它易于查看,更改并向非程序员解释;他们说的图片胜过千言万语!
记住状态机是一个抽象是很有用的,你不需要特殊的工具来创建一个,但是工具可能很有用 .
例如,您可以实现具有以下功能的状态机:
这台机器会搜寻海鸥并试图用水气球打它们 . 如果它错过它会尝试射击直到它击中(可以做一些现实的期望;)),否则它将在控制台中幸灾乐祸 . 它继续捕猎,直到它被海鸥骚扰 .
每个功能对应于每个状态;未显示开始和结束(或接受)状态 . 可能有更多的状态,而不是函数建模 . 例如,在发射气球之后,机器实际上处于与之前相比的另一种状态,但我认为这种区别是不切实际的 .
一种常见的方法是使用类来表示状态,然后以不同的方式连接它们 .
我还没有尝试在C#中实现FSM,但这些听起来(或看起来)非常复杂,因为我过去在C或ASM等低级语言中处理FSM的方式 .
我相信我一直都知道的方法叫做“迭代循环” . 在它中,你基本上有一个'while'循环,它根据事件(中断)定期退出,然后再次返回主循环 .
在中断处理程序中,您将传递一个CurrentState并返回一个NextState,然后在主循环中覆盖CurrentState变量 . 无限制地执行此操作直到程序关闭(或微控制器重置) .
我所看到的其他答案看起来都非常复杂,而在我看来,FSM是如何实现的;它的美丽在于它的简洁性和FSM可能非常复杂,有许多很多状态和转换,但它们允许复杂的过程容易被分解和消化 .
我意识到我的回答不应该包括另一个问题,但我不得不问:为什么这些其他提议的解决方案看起来如此复杂?
它们似乎类似于用巨大的大锤击打小钉子 .
在网上找到了这个很棒的教程,它帮助我绕过有限状态机 .
http://gamedevelopment.tutsplus.com/tutorials/finite-state-machines-theory-and-implementation--gamedev-11867
本教程与语言无关,因此可以轻松地适应您的C#需求 .
此外,使用的例子(寻找食物的 Ant )很容易理解 .
From the tutorial:
今天我深入国家设计模式 . 我做了并测试了ThreadState,它等于(/ - )在C#中的线程,如Threading in C#中的图片所述
您可以轻松地添加新状态,配置从一个状态到另一个状态的移动非常容易,因为它在状态实现中被封装
实施和使用于:Implements .NET ThreadState by State Design Pattern
什么回合StatePattern . 这符合您的需求吗?
我认为它的背景相关,但它确实值得一试 .
http://en.wikipedia.org/wiki/State_pattern
这让你的州决定去哪里,而不是“对象”类 .
布鲁诺
我刚刚贡献了这个:
https://code.google.com/p/ysharp/source/browse/#svn%2Ftrunk%2FStateMachinesPoC
这是演示直接和间接发送命令的示例之一,状态为IObserver(信号),因此响应信号源,IObservable(信号):
注意:此示例相当人为,主要用于演示许多正交特征 . 应该很少真正需要通过一个完整的类来实现状态值域本身,使用这样的CRTP(参见:http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) .
这是一个肯定更简单且可能更常见的实现用例(使用简单的枚举类型作为状态值域),对于相同的状态机,并且具有相同的测试用例:
https://code.google.com/p/ysharp/source/browse/trunk/StateMachinesPoC/WatchingTVSample.cs
“HTH
在我看来,状态机不仅用于改变状态,而且(非常重要)用于处理特定状态内的触发器/事件 . 如果你想更好地理解状态机设计模式,可以在书中找到一个很好的描述Head First Design Patterns, page 320 .
它不仅涉及变量中的状态,还涉及处理不同状态内的触发器 . 伟大的章节(不,我提到这个:-)没有任何费用,其中只包含一个易于理解的内容说明 .
我让朱丽叶's code. It'对我来说真是太棒了 .
这些是好处:
你可以在代码中用两个枚举
TState
和TCommand
创建新的状态机,添加了struct
TransitionResult<TState>
以更好地控制[Try]GetNext()
方法的输出结果暴露嵌套类
StateTransition
only 到AddTransition(TState, TCommand, TState)
使其更容易使用码:
这是TryGetNext方法的返回类型:
如何使用:
这是如何从泛型类创建
OnlineDiscountStateMachine
:为其状态定义枚举
OnlineDiscountState
,为其命令定义枚举OnlineDiscountCommand
.使用这两个枚举定义从泛型类派生的类
OnlineDiscountStateMachine
从
base(OnlineDiscountState.InitialState)
派生构造函数,以便 initial state 设置为OnlineDiscountState.InitialState
根据需要多次使用
AddTransition
使用派生状态机
我认为Juliet提出的状态机有一个错误:方法GetHashCode可以为两个不同的转换返回相同的哈希码,例如:
为避免此错误,该方法应如下所示:
亚历克斯
我会推荐state.cs . 我个人使用state.js(JavaScript版本)并且非常满意 . C#版本以类似的方式工作 .
您实例化状态:
您实例化一些转换:
您可以在状态和转换上定义操作:
那就是(差不多)它 . 查看网站了解更多信息 .
NuGet中有2个流行的状态机包 .
Appccelerate.StateMachine(13.6K下载3.82K的旧版本(bbv.Common.StateMachine))
StateMachineToolkit(下载1.56K)
Appccelerate lib有good documentation,但它不支持.NET 4,所以我为我的项目选择了StateMachineToolkit .