08a-枚举(Enum)
枚举是一组命名常量,用于表示一组有限的可选值。枚举是值类型,底层默认使用 int 存储。
一、基本定义和使用
csharp
// 基本定义
public enum Season { Spring, Summer, Autumn, Winter }
// 使用
Season current = Season.Summer;
Console.WriteLine(current); // Summer
Console.WriteLine((int)current); // 1(默认从 0 开始)
// 遍历所有枚举值
foreach (Season s in Enum.GetValues<Season>())
Console.WriteLine($"{s} = {(int)s}");
// Spring = 0, Summer = 1, Autumn = 2, Winter = 3指定底层类型和值
csharp
// 指定底层类型(默认 int)
public enum ErrorCode : byte { None, NotFound, Timeout }
// 手动指定值
public enum HttpStatus
{
OK = 200,
Created = 201,
BadRequest = 400,
NotFound = 404,
ServerError = 500
}
// 多个成员可共享同一值(互为别名)
public enum Days
{
Sunday = 0,
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6,
Weekend = 0 // 与 Sunday 同值
}支持的底层类型
| 类型 | 大小 | 说明 |
|---|---|---|
byte / sbyte | 1 字节 | 小范围枚举 |
short / ushort | 2 字节 | 中等范围 |
int / uint | 4 字节 | 默认,最常用 |
long / ulong | 8 字节 | 大范围枚举 |
二、Flags 枚举
使用 [Flags] 特性创建位掩码枚举,多个值可通过位运算组合。
csharp
[Flags]
public enum FileAccess
{
None = 0, // 000
Read = 1, // 001
Write = 2, // 010
Execute = 4, // 100
// 组合值
ReadWrite = Read | Write // 011 = 3
}
// 位运算组合
FileAccess permission = FileAccess.Read | FileAccess.Write;
// 检查是否包含某权限
bool canRead = (permission & FileAccess.Read) != 0; // true
bool canExecute = permission.HasFlag(FileAccess.Execute); // false
// 添加权限
permission |= FileAccess.Execute; // 现在包含 Execute
// 移除权限
permission &= ~FileAccess.Write; // 移除 Write
// 切换权限
permission ^= FileAccess.Read; // 移除 Read(原来有)
permission ^= FileAccess.Read; // 添加 Read(原来没有,来回切换)
// 输出:Read, Execute(逗号分隔)
Console.WriteLine(permission);Flags 枚举设计规范
csharp
[Flags]
public enum Permissions
{
None = 0, // 必须为 0
View = 1, // 2^0
Create = 2, // 2^1
Edit = 4, // 2^2
Delete = 8, // 2^3
Admin = View | Create | Edit | Delete // 组合
}三、枚举的常用操作
1. 字符串 ↔ 枚举
csharp
// 枚举 → 字符串
string name = Season.Summer.ToString(); // "Summer"
string name2 = Enum.GetName(Season.Summer); // "Summer"
// 字符串 → 枚举(Parse)
Season s = (Season)Enum.Parse(typeof(Season), "Summer"); // Summer
Season s2 = Enum.Parse<Season>("Summer"); // 泛型版本(C# 7.0+)
// 字符串 → 枚举(TryParse,推荐)
if (Enum.TryParse<Season>("Summer", out Season result))
Console.WriteLine(result); // Summer
// 忽略大小写
if (Enum.TryParse<Season>("summer", ignoreCase: true, out result))
Console.WriteLine(result); // Summer2. 枚举 ↔ 数值
csharp
// 枚举 → 数值
int val = (int)Season.Summer; // 1
// 数值 → 枚举
Season s = (Season)1; // Summer
Season s2 = (Season)999; // 不报错!即使没有对应值
// 安全判断:值是否合法
bool defined = Enum.IsDefined(typeof(Season), 1)); // true
bool defined2 = Enum.IsDefined(typeof(Season), 999)); // false3. 遍历枚举
csharp
// 获取所有枚举值数组
Season[] all = Enum.GetValues<Season>();
// 获取所有枚举名称
string[] names = Enum.GetNames<Season>(); // ["Spring","Summer","Autumn","Winter"]
// 遍历
foreach (Season s in Enum.GetValues<Season>())
Console.WriteLine($"{(int)s}: {s}");
// 获取成员数量
int count = Enum.GetNames<Season>().Length; // 4四、枚举与 switch 配合
csharp
// 传统 switch 语句
string GetSeasonMessage(Season season)
{
switch (season)
{
case Season.Spring:
return "春暖花开";
case Season.Summer:
return "夏日炎炎";
case Season.Autumn:
return "秋高气爽";
case Season.Winter:
return "冬日暖阳";
default:
return "未知季节";
}
}
// switch 表达式(更简洁)
string GetSeasonMessage2(Season season) => season switch
{
Season.Spring => "春暖花开",
Season.Summer => "夏日炎炎",
Season.Autumn => "秋高气爽",
Season.Winter => "冬日暖阳",
_ => "未知季节"
};注意: switch 处理枚举时最好包含所有成员 + default 分支,以适应未来新增枚举值的情况。
五、枚举的常用方法
| 方法 | 说明 | 示例 | 结果 |
|---|---|---|---|
Enum.GetValues<T>() | 获取所有枚举值数组 | GetValues<Season>() | [Spring, Summer, Autumn, Winter] |
Enum.GetNames<T>() | 获取所有枚举名称数组 | GetNames<Season>() | ["Spring",...] |
Enum.Parse<T>(string) | 字符串转枚举(抛异常) | Parse<Season>("Summer") | Summer |
Enum.TryParse<T>(...) | 字符串转枚举(安全) | TryParse("summer", out s) | true, s=Summer |
Enum.IsDefined<T>(value) | 检查值是否合法 | IsDefined<Season>(1) | true |
Enum.GetName<T>(value) | 获取值的名称 | GetName(Season.Summer) | "Summer" |
HasFlag(flag) | 检查是否包含标志(Flags) | perm.HasFlag(Read) | true/false |
ToString() | 枚举转字符串 | Summer.ToString() | "Summer" |
六、枚举 vs 其他方案
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 有限固定可选值 | enum | 类型安全,可读性好 |
| 需要位运算组合 | [Flags] enum | 支持 &、管道符等位操作 |
| 值来自外部(数据库/API) | const 或 static readonly | 枚举要求编译时确定 |
| 需要扩展新值(不修改源码) | enum + switch default | 但更好的方案是策略模式 |
| 大量动态值 | Dictionary<string, int> | 枚举值应有限且固定 |
七、综合案例:订单状态
csharp
public enum OrderStatus
{
Pending = 0,
Paid = 1,
Shipped = 2,
Delivered = 3,
Cancelled = 4,
Refunded = 5
}
public class Order
{
public string Id { get; set; }
public OrderStatus Status { get; set; }
public decimal Amount { get; set; }
public string GetStatusDescription() => Status switch
{
OrderStatus.Pending => "待支付",
OrderStatus.Paid => "已支付,待发货",
OrderStatus.Shipped => "已发货",
OrderStatus.Delivered => "已送达",
OrderStatus.Cancelled => "已取消",
OrderStatus.Refunded => "已退款",
_ => "未知状态"
};
public bool CanCancel() => Status switch
{
OrderStatus.Pending or OrderStatus.Paid => true,
_ => false
};
public void ProcessNext()
{
Status = Status switch
{
OrderStatus.Pending => OrderStatus.Paid,
OrderStatus.Paid => OrderStatus.Shipped,
OrderStatus.Shipped => OrderStatus.Delivered,
_ => Status
};
}
}核心知识点总结
枚举关键特性
| 特性 | 说明 |
|---|---|
| 值类型 | 继承自 System.Enum → System.ValueType |
| 底层存储 | 默认为 int,可指定 byte/short/long 等 |
| 默认值 | 第一个成员为 0(未显式赋值时) |
| 类型安全 | 不能将其他枚举类型隐式赋值给它 |
| 编译时确定 | 值在编译时固定,不能运行时新增 |
注意事项
- 枚举默认从 0 开始——未赋值时第一个成员为 0,后续依次递增
- 显示转换不会检查合法性——
(Season)999不会报错,用Enum.IsDefined()检查 - Flags 枚举需要 2 的幂——1, 2, 4, 8...,且必须包含
None = 0 - HasFlag 性能低于位运算——
(perm & Flag) != 0比perm.HasFlag(Flag)快 - Enum.Parse 会抛异常——不确定来源的字符串用
Enum.TryParse - switch 必须包含所有情况——加上 default 防止新增枚举值后遗漏
- 避免将枚举用于开放集合——枚举值应当固定且有限
- 命名用单数——普通枚举用单数(
Season),Flags 枚举用复数(FileAccess)


