【发布时间】:2010-11-14 18:09:18
【问题描述】:
使用yield关键字实现简单状态机as shown here是否可行。在我看来,C# 编译器似乎已经为您完成了艰苦的工作,因为它在内部实现了一个状态机来使 yield 语句工作。
您能否利用编译器已经在做的工作并让它为您实现大部分状态机?
有人做过吗,技术上可行吗?
【问题讨论】:
标签: c# yield state-machine fsm
使用yield关键字实现简单状态机as shown here是否可行。在我看来,C# 编译器似乎已经为您完成了艰苦的工作,因为它在内部实现了一个状态机来使 yield 语句工作。
您能否利用编译器已经在做的工作并让它为您实现大部分状态机?
有人做过吗,技术上可行吗?
【问题讨论】:
标签: c# yield state-machine fsm
这是可行的,但这是一个坏主意。创建迭代器块是为了帮助您为集合编写自定义迭代器,而不是解决实现状态机的通用问题。
如果你想写一个状态机,就写一个状态机。这并不难。如果您想编写大量状态机,请编写一个有用的帮助方法库,让您清楚地表示状态机,然后使用您的库。但是不要滥用一种语言结构,该结构旨在用于完全不同的东西,而恰好使用状态机作为实现细节。这使您的状态机代码难以阅读、理解、调试、维护和扩展。
(顺便说一句,当我读到你的名字时,我做了一个双重考虑。C# 的设计者之一也叫 Matt Warren!)
【讨论】:
RegEx;本质上,将其用作 DFA 之类的东西。您对此有何看法?
是的,这绝对是可能的,而且很容易做到。您可以享受使用控制流构造(for、foreach、while、...goto(使用goto 特别适合这种情况;)))以及yields 来构建一个。
IEnumerator<State> StateMachine
(Func<int> currentInput /* gets current input from IO port */,
Func<int> currentOutput) {
for (;;) {
if ((currentInput() & 1) == 0)
yield return new State("Ready");
else {
if (...) {
yield return new State("Expecting more data");
SendOutput(currentOutput());
while ((currentInput() & 2) != 0) // while device busy
yield return new State("Busy");
else if (...) { ... }
}
}
}
// consumer:
int data;
var fsm = StateMachine(ReadFromIOPort, () => data);
// ...
while (fsm.Current != "Expecting more data")
fsm.MoveNext();
data = 100;
fsm.MoveNext();
【讨论】:
ReadFromIOPort 延迟、端口意外关闭或端口正忙于其他事情,此状态机会发生什么情况?如果要设置接收数据的超时时间怎么办?必须改变机器的循环特性以处理任何问题。
迭代器块确实实现了状态机,但棘手的是获取下一个输入。你怎么知道下一步要去哪里?我猜你可能有某种共享的“当前转换”变量,但这有点恶心。
如果您不需要任何输入(例如,您的状态机只是在状态之间循环),那么这很容易,但这不是有趣的类型 :)
您能描述一下您感兴趣的状态机类型吗?
【讨论】:
虽然这不是经典意义上的状态机,但关于 Iterator-based Micro Threading 的文章创造性地使用了 yield 来实现基于状态的操作。
IEnumerable Patrol ()
{
while (alive){
if (CanSeeTarget ()) {
yield return Attack ();
} else if (InReloadStation){
Signal signal = AnimateReload ();
yield return signal;
} else {
MoveTowardsNextWayPoint ();
yield return TimeSpan.FromSeconds (1);
};
}
yield break;
}
【讨论】: