enum可声明一组带有相同值类型标签、数值有规律、关联性强的常量,替代“魔法数字”,提高代码的可读性。
enum还可以设计结构体,结合数组可模拟查改结构体属性,方便用户管理复杂数据。
若未来需增减数据,只需修改枚举定义,无需修改所有用到数字索引的代码。
编译器会对枚举相关代码进行更加灵活的类型标签检查,引导用户编写正确代码。
<enum> \常量标签覆盖/ \枚举类名/ \升值器/ <{> \枚举常量声明/ <}> \;/
<值类型标签名称> <:>
<(> \<升值型赋值运算符> <常量表达式>/ <)>
\属性标签覆盖/ \枚举常量名称/ \属性尺寸声明/ \初始化表达式/ \,/
<[> <常量表达式> <]>
<=> <常量表达式>
用途:定义一组有意义,强相关的常量,代替 0, 1, 2... 这样的数字,提高代码可读性。
enum LibType
{
LibType_Library,
LibType_Class
};
// 参数2带有LibType标签,暗示用户填写LibType枚举的成员,若填写无关的表达式,则会触发警告
native LibraryExists(const library[], LibType:type);
static gErrorText[128];
public plugin_natives()
{
// 调用LibraryExists函数,参数2根据需求填LibType_Library或LibType_Class
if (!LibraryExists("zombienpc", LibType_Library))
{
formatex(gErrorText, charsmax(gErrorText), "'ZombieNpc' plugin must initialize earlier than the current plugin. Check plugins.ini.");
return;
}
}
反面教材:定义一组强相关的宏常量,在没有注释的情况下,用户根本猜不出这些宏常量的用途。
#define AMX_ERR_NATIVE 10
#define AMX_ERR_MEMACCESS 5
#define AMX_ERR_NONE 0
#define AMX_ERR_BOUNDS 4
#define AMX_ERR_STACKERR 3
#define AMX_ERR_STACKLOW 7
#define AMX_ERR_HEAPLOW 8
#define AMX_ERR_DIVIDE 11
#define AMX_ERR_NOTFOUND 19
#define AMX_ERR_PARAMS 25
#define AMX_ERR_GENERAL 27
// 参数1带有_标签,用户绝对猜不到要填写名称以AMX_ERR_开头的宏常量
native abort(error, const fmt[] = "", any:...);
public plugin_precache()
{
if (gErrorText[0] != EOS)
{
// 填写任何带有_标签的表达式作为参数1,都不会触发警告.原生inc文件中存在大量这样的缺陷设计
abort(AMX_ERR_NONE, gErrorText);
}
}
用途:定义二进制位标志,表示可并存的状态,比如同时按下多个按键,同时拥有多种权限。
/// 障碍物特征
enum ObstacleAttribute (<<= 1) // 每次左移1位(相当于*= 2)
{
/// 值为0,表示无任何特征,无法与其它特征并存
OA_Static,
/// 显式赋值为1,让接下来的常量可以自动赋值 (二进制 0b_0001)
OA_Dynamic = 1,
/// 自动赋值为2 (0b_0010)
OA_Ladder,
/// 自动赋值为4 (0b_0100)
OA_Virtualized
}
// 表示某个障碍物同时含有的特征:运动中的爬梯,由于被虚化而无法产生碰撞 值为1|2|4=7 (0b_0111)
new ObstacleAttribute:obsAttributes = OA_Dynamic | OA_Ladder | OA_Virtualized;
用途:用 enum + 数组模拟 C 语言的 struct,方便管理复杂数据。
// 僵尸外观数据结构
enum tZombieAppearance
{
_: ZA_MdlPath[64],
_: ZA_BodyId,
_: ZA_SkinId,
bool: ZA_SunkInGround
};
// 僵尸类型数据结构
enum tZombieClass
{
_: ZC_Name[32],
// 属性尺寸声明要求[]内填写带有_标签的常量表达式,表示结构体属性的尺寸
// 若填写枚举类名,表示该这个结构体属性也是结构体.编译器自动切换tZombieAppearance的标签为_
_: ZC_Appearance[tZombieAppearance],
_: ZC_MaxHealth,
_: ZC_MaxSpeed,
Float: ZC_EyePos[3]
};
// 维度声明要求[]内填写带有_标签的常量表达式,编译器自动切换tZombieClass的标签为_
// tZombieClass让2维数组gDefaultZombieClasses内的1维数组变成结构体,
// 解析器允许用户使用结构体字面量对这些1维数组进行初始化
static gDefaultZombieClasses[][tZombieClass] =
{
// 第1个结构体字面量
{
"普通僵尸",
{
"models/player/zombie_classic/zombie_classic.mdl",
0,
0,
true
},
1000,
250,
{ 0.0, 0.0, 35.0 }
},
// 第2个结构体字面量
{
"快速僵尸",
{
"models/zombie_fast.mdl",
0,
0,
false
},
500,
320,
{ 0.0, 0.0, 0.0 }
}
};
static Array:gZombieClasses = Invalid_Array;
native Array:ArrayCreate(cellsize = 1, reserved = 32);
native ArrayPushArray(Array:which, const any:input[], size = -1);
// native ZombieClasses_Add(zombieClass[tZombieClass]);
public @ZombieClasses_Add(pluginId, numParams)
{
// tZombieClass让1维数组zombieClass变成结构体
new zombieClass[tZombieClass];
get_array(1, zombieClass, sizeof zombieClass);
if (gZombieClasses == Invalid_Array)
{
// ArrayCreate的参数1要求填写带有_标签的表达式,编译器自动切换tZombieClass的标签为_
gZombieClasses = ArrayCreate(tZombieClass);
}
// 用ZC_Name访问1维数组zombieClass的元素,允许额外添加[]索引器访问元素的元素
// ZC_Name值为0,但若之间填0,会触发标签不匹配的警告,且第二个[]索引器会触发语法错误
// zombieClass本质是1维数组,不可能升维,这两个索引器会被解析为[ZC_Name + 0]
if (zombieClass[ZC_Name][0] == EOS)
{
log_error(AMX_ERR_NATIVE, "The name of zombie class connot be empty.");
return;
}
// ZC_Appearance让结构体属性也变成了结构体,允许使用第二,甚至第三个[]索引器
if (zombieClass[ZC_Appearance][ZA_MdlPath][0] == EOS)
{
log_error(AMX_ERR_NATIVE, "The appearance of zombie classes cannot use empty model paths.");
return;
}
// 参数2要求索引器使用_标签,编译器自动切换zombieClass索引器标签为_
ArrayPushArray(gZombieClasses, zombieClass);
}
访问结构体属性时,可使用sizeof关键字获取该属性尺寸。
比起直接填写具体尺寸数值,sizeof的可读性更高。
server_print("僵尸名称尺寸:%d, 实际单元数:%d", sizeof zombieClass[ZC_Name], strlen(zombieClass[ZC_Name]));