C# 中的枚举(Enum)学习笔记
1. 基础概念与用法
1.1 什么是枚举(Enum)?
枚举(Enum) 是 C# 中一种特殊的值类型,用于定义一组命名的常量。它提供了一种更清晰、更安全的方式来表示一组有限的、固定的选项。
例如:
- 一周的星期:
Monday、Tuesday… - 用户状态:
Active、Inactive、Pending - 颜色类型:
Red、Green、Blue
使用枚举可以让代码更具可读性、减少错误(避免使用魔法数字或字符串),并提升类型安全性。
1.2 如何定义枚举?
使用 enum 关键字定义:
enum Weekday
{
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}
默认情况下,枚举成员从 0 开始递增(Monday = 0, Tuesday = 1, …)。
你也可以显式指定值:
enum Status
{
Pending = 0,
Approved = 1,
Rejected = 2,
Archived = 100 // 可以跳过数字
}
还可以指定底层类型(默认是 int):
enum SmallStatus : byte
{
Off = 0,
On = 1
}
支持的底层类型包括:byte、sbyte、short、ushort、int、uint、long、ulong。
1.3 基本使用示例
声明与赋值
Weekday today = Weekday.Friday;
Console.WriteLine(today); // 输出:Friday
转换为整数或从整数转换
int dayNumber = (int)Weekday.Wednesday; // 2
Weekday day = (Weekday)3; // Thursday
⚠️ 注意:强制转换可能产生无效枚举值(如 (Weekday)999),需谨慎。
1.4 常用方法与操作
1. 获取所有枚举值(Enum.GetValues)
foreach (Weekday day in Enum.GetValues(typeof(Weekday)))
{
Console.WriteLine(day);
}
2. 获取所有名称(Enum.GetNames)
string[] names = Enum.GetNames(typeof(Weekday));
foreach (string name in names)
{
Console.WriteLine(name);
}
3. 将字符串转为枚举(Enum.Parse / Enum.TryParse)
string input = "Friday";
Weekday day = (Weekday)Enum.Parse(typeof(Weekday), input);
// 更安全的方式:使用 TryParse
if (Enum.TryParse(input, out Weekday result))
{
Console.WriteLine($"解析成功: {result}");
}
else
{
Console.WriteLine("无效的枚举名称");
}
✅ 建议使用
TryParse,避免因无效字符串抛出异常。
2. 进阶知识点
2.1 枚举支持“位标志”(Flags)
当你需要组合多个选项时(如文件权限:读 + 写 + 执行),可以使用 [Flags] 特性。
定义方式
[Flags]
enum FileAccess
{
None = 0,
Read = 1, // 0001
Write = 2, // 0010
Execute = 4, // 0100
ReadWrite = Read | Write // 0011 = 3
}
💡 成员值必须是 2 的幂次(1, 2, 4, 8, 16...),以便进行位运算。
使用示例
FileAccess access = FileAccess.Read | FileAccess.Write;
Console.WriteLine(access); // 输出:ReadWrite
// 检查是否包含某个权限
bool canRead = access.HasFlag(FileAccess.Read); // true
⚠️
HasFlag性能较低,对性能敏感场景建议用位运算:(access & FileAccess.Read) != 0
2.2 枚举与字符串的友好显示(使用 DescriptionAttribute)
默认 ToString() 返回枚举名称,但有时你想显示更友好的中文或描述。
示例:带描述的枚举
using System.ComponentModel;
enum OrderStatus
{
[Description("待支付")]
Pending,
[Description("已发货")]
Shipped,
[Description("已完成")]
Completed
}
获取描述的辅助方法
public static string GetDescription(Enum value)
{
var field = value.GetType().GetField(value.ToString());
var attribute = (DescriptionAttribute)Attribute.GetCustomAttribute(
field, typeof(DescriptionAttribute));
return attribute?.Description ?? value.ToString();
}
使用:
Console.WriteLine(GetDescription(OrderStatus.Shipped)); // 输出:已发货
2.3 枚举的局限性
- 枚举不能继承(隐式继承
System.Enum,而System.Enum是抽象类)。 - 枚举不能定义方法、属性或构造函数(C# 11 之前;C# 11 起支持枚举上的扩展成员,但功能仍有限)。
- 枚举不是类型安全的到极致:你可以强制转换任意整数为枚举,即使它不在定义中。
🛡️ 最佳实践:在关键逻辑中,使用
Enum.IsDefined验证值是否合法。
if (Enum.IsDefined(typeof(Weekday), 999))
{
// 不会进入这里
}
2.4 C# 11+ 新特性:枚举上的静态抽象成员(预览/有限支持)
虽然目前仍不能在枚举中直接写方法,但可通过扩展方法或泛型约束模拟行为。
例如,为所有枚举添加 IsValid 扩展:
public static class EnumExtensions
{
public static bool IsValid<T>(this T value) where T : struct, Enum
{
return Enum.IsDefined(typeof(T), value);
}
}
// 使用
Weekday day = (Weekday)999;
Console.WriteLine(day.IsValid()); // False
3. 实际工作中的使用场景
场景 1:表示状态机(State Machine)
解释:在业务流程中(如订单、审批、任务),对象常处于不同状态。使用枚举可以清晰定义状态流转。
enum OrderState
{
Created,
Paid,
Shipped,
Delivered,
Cancelled
}
class Order
{
public OrderState State { get; set; }
public void Ship()
{
if (State == OrderState.Paid)
State = OrderState.Shipped;
else
throw new InvalidOperationException("只有已支付的订单才能发货!");
}
}
✅ 优点:防止非法状态转换,代码可读性强。
场景 2:配置选项或设置项
解释:程序中常有“模式”选择,如日志级别、环境类型、加密方式等。
enum LogLevel
{
Debug,
Info,
Warning,
Error
}
void Log(LogLevel level, string message)
{
if (level >= LogLevel.Warning)
{
Console.WriteLine($"[{level}] {message}");
}
}
✅ 优点:比字符串更安全,避免拼写错误(如 "warnning")。
场景 3:权限/角色控制(配合 [Flags])
解释:用户权限常是多个选项的组合(如“可读+可写”),[Flags] 枚举天然适合此场景。
[Flags]
enum UserPermission
{
None = 0,
View = 1,
Edit = 2,
Delete = 4,
Admin = View | Edit | Delete
}
UserPermission userPerm = UserPermission.View | UserPermission.Edit;
if (userPerm.HasFlag(UserPermission.Edit))
{
// 允许编辑
}
✅ 优点:节省存储空间(一个整数表示多个权限),逻辑清晰。
场景 4:API 请求参数标准化
解释:在 Web API 中,前端传入的参数(如 sort=asc)可映射为枚举,避免字符串比较错误。
enum SortDirection
{
Asc,
Desc
}
[ApiController]
[Route("api/products")]
public class ProductController : ControllerBase
{
[HttpGet]
public IActionResult Get([FromQuery] SortDirection sort = SortDirection.Asc)
{
// 根据 sort 执行不同排序逻辑
return Ok();
}
}
✅ 优点:自动验证输入合法性,Swagger 等工具能自动生成文档选项。
场景 5:替代“魔法数字”或“魔法字符串”
解释:避免代码中出现 if (status == 2) 这种难以理解的写法。
❌ 不推荐:
if (user.Status == 1) { ... } // 1 是什么意思?
✅ 推荐:
if (user.Status == UserStatus.Active) { ... } // 一目了然
总结
| 项目 | 说明 |
|---|---|
| 本质 | 命名的整型常量集合 |
| 优点 | 提高可读性、类型安全、避免魔法值 |
| 常用操作 | Parse / TryParse、GetValues、HasFlag |
| 进阶技巧 | [Flags] 位枚举、Description 友好显示、扩展方法 |
| 典型场景 | 状态管理、权限控制、配置选项、API 参数 |
🌟 给初学者的建议:
- 优先使用枚举代替
int或string表示固定选项。- 组合选项时记得加
[Flags]并使用 2 的幂次赋值。- 始终用
TryParse而非Parse处理用户输入。- 不要滥用:选项超过 20 个或频繁变化时,考虑用数据库表或配置文件。

未来的所有日子,每当我想起你的时候,我的心里将会是一个盛大而永恒的春天 我们不是春季限定,你就是我的春天
浙公网安备 33010602011771号