C# 3.0下有限状态机的一种优雅的实现

实现状态机有多种模式,其中最灵活而强大的方式是通过迁移表来实现,该方式的缺点之一是需要编写大量小块代码去支持迁移表。而在C#3.0中,可以以一种非常优雅的方式实现。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace StateMachine
{
    class Program
    {
        static void Main(string[] args)
        {
            var door = new Door(State.Open);

while (true)
            {
                string s = Console.ReadLine();
                Operation op = string.IsNullOrEmpty(s) ? Operation.Push : Operation.Pull;
                door.Process(op);
            }
        }
    }

enum Operation
    {
        Push, Pull
    }

enum State
    {
        Open, Closed
    }

class Door
    {
        public State State { get; set; }

Dictionary<State, Dictionary<Operation, Action>> rule;
        public Door(State state)
        {
            this.State = state;

rule = new Dictionary<State, Dictionary<Operation, Action>>();
            foreach (var e in Enum.GetValues(typeof(State)))
            {
                rule[(State)e] = new Dictionary<Operation, Action>();
            }

InitOperationRule();
        }

void InitOperationRule()
        {
            //正常操作
            rule[State.Closed][Operation.Push] = () => { Console.WriteLine("门被推开了"); State = State.Open; };
            rule[State.Open][Operation.Pull] = () => { Console.WriteLine("门被拉上了"); State = State.Closed; };

加入几种特殊情况的处理
            //rule[State.Closed][Operation.Pull] = () => Console.WriteLine("门是关上的,拉了也白拉");
            //rule[State.Open][Operation.Push] = () => Console.WriteLine("门是开的,不用推了,直接进去吧");
        }

public void Process(Operation op)
        {
            try
            {
                rule[State][op]();
            }
            catch (KeyNotFoundException)
            {

Console.WriteLine(string.Format("门在{0}状态下不允许{1}操作", State, op));
            }
            
        }
    }
}

从代码中可以看到,通过lambda表达式,可以简化迁移表的构造,并且更加直观。

通过迁移表构造状态机的一种不足在于查询速度,在本例中每个操作都要进行两次查询才能进行状态转换操作。如果状态较多则非常费时,这里我把它改进了一下,使得每次操作只需要查询一次即可。

class DoorPlus
    {
        State state;
        public State State
        {
            get { return state; }
            set
            {
                if (state != value)
                    currentOpRule = rule[value];
                state = value;
            }
        }

Dictionary<Operation, Action> currentOpRule;
        Dictionary<State, Dictionary<Operation, Action>> rule;
        public DoorPlus(State state)
        {
            this.State = state;

rule = new Dictionary<State, Dictionary<Operation, Action>>();
            foreach (var e in Enum.GetValues(typeof(State)))
            {
                rule[(State)e] = new Dictionary<Operation, Action>();
            }

currentOpRule = rule[State];

InitOperationRule();
        }

void InitOperationRule()
        {
            //正常操作
            rule[State.Closed][Operation.Push] = () => { Console.WriteLine("门被推开了"); State = State.Open; };
            rule[State.Open][Operation.Pull] = () => { Console.WriteLine("门被拉上了"); State = State.Closed; };

加入几种特殊情况的处理
            //rule[State.Closed][Operation.Pull] = () => Console.WriteLine("门是关上的,拉了也白拉");
            //rule[State.Open][Operation.Push] = () => Console.WriteLine("门是开的,不用推了,直接进去吧");
        }

public void Process(Operation op)
        {
            try
            {
                currentOpRule[op]();
            }
            catch (KeyNotFoundException)
            {

Console.WriteLine(string.Format("门在{0}状态下不允许{1}操作", State, op));
            }
        }
    }

C# 3.0下有限状态机的一种优雅的实现相关推荐

  1. Linux下MySQL的几种安装方式

    闲来有空,整理下Linux下Mysql的几种安装方式,分别使用yum/rpm.常规方式编译安装.cmake方式编译安装以及使用二进制方式免编译安装MySQL Linux系统环境: CentOS rel ...

  2. [RHEL] RHEL7.0 下 Postfix + Dovecot 实现邮件发送

    RHEL7.0 下 Postfix + Dovecot 实现邮件发送 一.前言 大家都对邮件服务(mail service)很感兴趣嘛.我在自己 博客站 预言了自己会实战一次,访问量一天到十几(毕竟平 ...

  3. 【Android 逆向】ART 脱壳 ( InMemoryDexClassLoader 脱壳 | 加固厂商在 ART 下使用的两种类加载器 | InMemoryDexClassLoader 源码 )

    文章目录 一.加固厂商在 ART 下使用的两种类加载器 ( InMemoryDexClassLoader | DexClassLoader ) 二.InMemoryDexClassLoader 源码分 ...

  4. CE6.0 下获得 SD 卡序列号的方法

    经常在坛子里看到讨论软件加密的帖子,纯软件加密与读取硬件序列号加密是经常讨论到的. 两种方法各有优缺点. 在通过读取硬件序列号的方法来加密的方法,受硬件的限制. 一般来说,CPU和T-Flash可能存 ...

  5. android6.0源码分析之Camera API2.0下的Preview(预览)流程分析

    1.Camera2 preview的应用层流程分析 preview流程都是从startPreview开始的,所以来看startPreview方法的代码: <code class="hl ...

  6. 深入讲解JSP 2.0下的动态内容缓存技术

    深入讲解JSP 2.0下的动态内容缓存技术 来源: 山里娃信息网  新闻题要: 内容缓存是Web应用中最普通的优化技术之一,例如,可以使用一个自定义地JSP标签--我们将之命名为<jc:cach ...

  7. 关于.Net2.0下配置架构的使用

    上次用到配置文件,就花了一些时间研究了一下.Net2.0下的配置文件架构,当时感觉确实很强大,完善,但看的有些头晕.迷迷糊糊把实现了要求,就没有再深入研究. 最近,想在配置文件里实现一个复杂的配置,多 ...

  8. 图解WinCE6.0下的内核驱动和用户驱动

    图解WinCE6.0下的内核驱动和用户驱动 在<WinCE驱动程序的分类>中曾提到,WinCE6.0的流驱动既可以加载到内核态也可以加载到用户态.下面通过一组图片简单说明一下这两种驱动的关 ...

  9. 一步一步教你在CentOS6.0下安装NS2(ns-allinone-2.34.tar.gz)模拟仿真工具

    在CentOS6.0(内核版本2.6.32-71.el6.i686 )发行版中,基于2.6.32的内核协议栈部分做了适当调整,因此我们在选择ns2的安装版本时就不可以选择较老的版本,因为很多东西都编译 ...

最新文章

  1. 怎样做好敏捷项目管理?
  2. MapReduce实现Apriori算法
  3. crio电压采集 labview_NI cDAQ917采集温度方法
  4. 现代软件工程 作业 2 结对项目
  5. 信息学奥赛一本通(1181:整数奇偶排序)——快速排序
  6. ROS2官网安装教程补充
  7. jquery获取前一个月日期
  8. linux python指令大全,Linux常用指令
  9. 爱卡创誓记java饰品,《创誓记AIKA》芙兰精灵配上框架眼睛折服宅男宅女
  10. Android Ptrace Inject
  11. MPU6050读取实验
  12. 公摊面积用计算机怎么计算,公摊面积计算(公摊面积计算器)
  13. chrome浏览器关闭更新弹窗
  14. nagios的配置官方文档篇
  15. uniapp nvue运用map组件实现地图标注以及检索周边地址
  16. 递归陷入死循环的判断方法与解决措施(java代码)
  17. 关于多径效应,平坦衰落,频率选择性衰落以及瑞利衰落的理解
  18. 文本生成任务常见评估指标
  19. 【转载】人生如梦游戏间,RPG游戏开源开发讲座(JAVA篇)[3]——邯郸学步
  20. outlook邮件开发开发HTML,CSS标签及样式失效总结

热门文章

  1. libpcap-mmap分析(五)
  2. 小程序接收json_微信推出QQ小程序,但只能接收消息无法回复!网友:鸡肋
  3. python用一行代码编写一个回声程序_一行python代码实现树结构
  4. 为什么道理都懂,却还是一事无成
  5. Linux(CentOS 5)下安装Oracle10 客户端(转)
  6. 昆仑网(D×××)去中心化虚拟互联网基础功能图片式介绍,请全屏看。
  7. 使用StarWind为RAC设置存储
  8. 内置付费 inapp purchase and Error Domain=SKErrorDomain Code=0 “Cannot connect to iTunes Store”错误...
  9. ActionScript 3.0 Step By Step系列(一):工欲其善,先利其器(Flex Builder)
  10. md5sum算法 —— linux或Unix上,md5sum是用来计算和校验文件报文摘要的工具程序