一、枚举类型Enum的基本定义与使用
本来,C#的枚举类型没什么好说的,就是刚学编程的都会,甚至还会问,这废东西还不如写一大堆常量,拿回C++那套:
public const int A = 0;
public const int B = 1;
public const int C = 2;
不过确实,枚举类型就是由C++传统这套拿过来的。先不说它有什么好处,至少数组化、结构体化、类化之后,枚举可以使代码更易于维护,代码更清晰,有助于确保给变量指定合法的、期望的值。同时,你还需要如下代码:
using System;
using System.Collections.Generic;
using System.Text;
namespace Enumerate
{
enum ABC
{
A = 0,
B = 1,
C,
};
class Program
{
static void Main(string[] args)
{
Console.WriteLine((int)ABC.C);
}
}
}
运行结果如下:
需要注意的是:
1、我不用再于ABC这个枚举类型enum中罗列C=2,C#会自动识别C=2的。
2、如果你要输出C=?,你必须将ABC.C转化成int。否则,如果你的代码写成这样,在输出ABC.C之前缺少一个强制类型转换的(int):
using System;
using System.Collections.Generic;
using System.Text;
namespace Enumerate
{
enum ABC
{
A = 0,
B = 1,
C,
};
class Program
{
static void Main(string[] args)
{
Console.WriteLine(ABC.C);
}
}
}
你的输出是这样的:
二、枚举类型Enum在实际编程中的简单应用
当然,上述也只是一些小问题的小打小闹。并没有看出枚举类型Enum有什么卵用,但是如果你将这个枚举类型和《【C++】状态模式》(点击打开链接)这种设计模式联系起来,则大有用途。尤其在诸如Unity3D等需要一个死循环while支撑游戏编程中,也就是那些程序不关,循环不灭的程序中,状态设计模式的思想将被采用,枚举类型Enum同样也会大放异彩。
先写个简单的。
using System;
using System.Collections.Generic;
using System.Text;
namespace Enumerate
{
enum ABC
{
A = 0,
B = 1,
C,
};
class Program
{
static void Main(string[] args)
{
ABC abc = ABC.A;
while (true)
{
switch (abc)
{
case ABC.A:
Console.WriteLine("请输入B或者C进入状态B或者C,否则将处于状态A");
string input = Console.ReadLine();
if (input.Equals("B") || input.Equals("b"))
{
abc = ABC.B;
}
else if (input.Equals("C") || input.Equals("c"))
{
abc = ABC.C;
}
else
{
Console.WriteLine("还是处于状态A");
abc = ABC.A;
}
break;
case ABC.B:
Console.WriteLine("来到状态B了,输出完成~回到状态A,继续期待你的光临");
abc = ABC.A;
break;
case ABC.C:
Console.WriteLine("来到状态C了,输出完成~回到状态A,继续期待你的光临");
abc = ABC.A;
break;
}
}
}
}
}
运行结果如下:
此程序的平常状态为A,随时接受用户,或者说,随时等待着用户的输入,也就是在轮询。当用户输入特定的值,或者你可以深刻理解成:一旦遇到某种条件,程序将从平常状态A转换成状态B或者C处理别的事情,处理完成再转化成平常状态A。
举这个例子,我相信已经很好地模拟出一些游戏编程中永远在循环读取的update()函数,每一个游戏帧执行的update()类似就是类似于这里的while(true),而,如果你需要在update()函数中处理一些类似游戏主角、游戏对象或者游戏状态的东西,建议利用这种状态设计模式。
通过阅读上述代码,相信大家大致能看到枚举类型Enum与内含switch~case结构的死循环while(true)配合形成的设计模式。这里,枚举组,叫做ABC,那么你就可以利用ABC a=ABC.A,来定义一个类似指向这个枚举组的指针a,先指向枚举组ABC的A,然后a=ABC.某某,就指向其它状态了,可以随心所欲在状态中切换,就可以省去一大堆,连你自己都绕翻车的if~else if ~else渣结构了。
三、枚举类型Enum与状态设计模式
以上的代码,体现了一种状态设计模式的思想,当然,虽然这类代码更常见,但是,这并不是严格的状态设计模式。如果要写一个严格的状态设计模式,同样可以用到枚举类型Enum。还是用那道2011年下半年的软件设计师软考题目再来说明:
题目是这样的:
某大型商场内安装了多个简易的纸巾售卖机,自动出售2元钱一包的纸巾,且每次仅售出一包纸巾,纸巾售卖机的状态图如图5-1所示:
采用状态(State)模式来实现该纸巾售卖机,得到如图5-2所示的类图,其中类State为抽象类,定义了投币、退币、出纸巾等方法接口。类SoldOutState、NoQuarterState、HasQuarterState、SoldState分别对应图5-1纸巾售卖机的4种状态。售出纸巾、纸巾售卖、没有投币、有2元钱。
当然,这里我就不完全照抄软考题目中提供的伪代码格式了,自己用枚举类型Enum写完这个状态设计模式,满足题目的要求。
首先,你要注意到,这是一个纸巾售卖机,就算其处于待机状态,一旦有用户来用,你也要即时相应,也时刻就处于轮询状态,所以这是就考虑到先枚举几个状态,对于用户的动作,时刻做出切换,因此,代码如下,注意到void dispense();实质是TissueMachine自己的内部处理方法,不会给用户调用的。只是这里接口要求其继承者,对其所含有的方法的可访问xing
using System;
namespace EnumStatePattern
{
interface StateHandle
{
void insertQuarter();//“投币”按钮被按下
void ejectQuarter();//“退币”按钮被按下
void turnCrank();//“出纸巾”按钮被按下
void dispense();//正在售出纸巾
}
class TissueMachine : StateHandle
{
private enum State
{//自动售货机的四种可能状态
soldOutState = -1,//纸巾卖完
noQuarterState = 0,//没有投币
hasQuarterState = 1,//已有投币
soldState = 2,//正在售出
}
private int money;//自动售货机当前拥有的投币数量
private State state;//自动售货机当前的状态
public TissueMachine()
{//自动售货机的初始化
this.money = 0;
this.state = State.noQuarterState;
}
public void insertQuarter()
{
switch (this.state)
{
case State.soldOutState:
Console.WriteLine("纸巾已经卖完了,本纸巾自动售货机停机!");
break;
case State.noQuarterState:
this.money += 2;
Console.WriteLine("感谢投币!当前纸巾机存有币:{0}", this.money);
this.state = State.hasQuarterState;
break;
case State.hasQuarterState:
this.money += 2;
Console.WriteLine("感谢投币!当前纸巾机存有币:{0}", this.money);
break;
case State.soldState:
Console.WriteLine("正在售出!请不要进行操作。");
break;
}
}
public void ejectQuarter()
{
switch (this.state)
{
case State.soldOutState:
Console.WriteLine("纸巾已经卖完了,本纸巾自动售货机停机!");
break;
case State.noQuarterState:
Console.WriteLine("投币机没有投币,无法退币!");
break;
case State.hasQuarterState:
this.money -= 2;
Console.WriteLine("你的投币已经退回!当前纸巾机存有币:{0}", this.money);
if (this.money == 0)
{
this.state = State.noQuarterState;
}
break;
case State.soldState:
Console.WriteLine("正在售出!请不要进行操作。");
break;
}
}
public void turnCrank()
{
switch (this.state)
{
case State.soldOutState:
Console.WriteLine("纸巾已经卖完了,本纸巾自动售货机停机!");
break;
case State.noQuarterState:
Console.WriteLine("请投币!");
break;
case State.hasQuarterState:
this.state = State.soldState;
Console.WriteLine("开始售出纸巾");
dispense();
break;
case State.soldState:
Console.WriteLine("正在售出!请不要进行操作。");
break;
}
}
public void dispense()
{
this.money -= 2;
Console.WriteLine("你的纸巾,请拿好!当前纸巾机存有币:{0}", this.money);
if (this.money == 0)
{
this.state = State.noQuarterState;
}
else
{
this.state = State.hasQuarterState;
}
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("输入1投币,输入2退币,输入3售出纸巾");
TissueMachine tissueMachine = new TissueMachine();
while (true)
{
switch (int.Parse(Console.ReadLine()))
{
case 1:
tissueMachine.insertQuarter();
break;
case 2:
tissueMachine.ejectQuarter();
break;
case 3:
tissueMachine.turnCrank();
break;
}
}
}
}
}
运行结果如下图所示:
大家可以注意到,上述代码的主函数非常非常简单,非常简单的同时,也非常清晰,用户输入什么则执行什么方法。
class Program
{
static void Main(string[] args)
{
Console.WriteLine("输入1投币,输入2退币,输入3售出纸巾");
TissueMachine tissueMachine = new TissueMachine();
while (true)
{
switch (int.Parse(Console.ReadLine()))
{
case 1:
tissueMachine.insertQuarter();
break;
case 2:
tissueMachine.ejectQuarter();
break;
case 3:
tissueMachine.turnCrank();
break;
}
}
}
}
而在TissueMachine这个类当中,一开始就使用了枚举类型Enum清晰罗列了4种基本状态。
private enum State
{//自动售货机的四种可能状态
soldOutState = -1,//纸巾卖完
noQuarterState = 0,//没有投币
hasQuarterState = 1,//已有投币
soldState = 2,//正在售出
}
在TissueMachine接下来的各个方法中,皆使用switch~case结构,针对TissueMachine处于不同的状态而作出不同相应。用户执行某些方法,在某特定的状态的话,会发生状态转换,从而保证这个方法的顺利执行。
这样的代码写作,由于利用枚举类型Enum与状态设计模式,使得程序猿写得清晰的同时,其它维护的程序猿也清晰。彻底可以忘掉,都能把自己绕死的if~else if~else结构,吃力还不讨好。
针对TissueMachine永远在轮询用户输入的类,它注定要处于无限循环之中,所以我们要以状态划分其处理方法。此时枚举类型Enum就派上用场了。