目 录CONTENT

文章目录

CSharp(三十八) 枚举(Enum)详解 —— 定义、使用与常用方法

CSharp(三十八) 枚举(Enum)详解 —— 定义、使用与常用方法

一、什么是枚举?

枚举是一组有名字的整数常量,用来给一组相关的选项起一个有意义的名字。

打个比喻:

  • 不用枚举时:int direction = 1; —— 你只知道 1,但 1 是什么?上是 1 还是下是 1?得查文档。
  • 用枚举后:Direction direction = Direction.Up; —— 一眼就知道是"向上"!

枚举就是把"魔法数字"变成"有意义的名字",让代码可读、可维护、不容易写错


二、定义枚举

enum 关键字定义:

// 基本定义
enum Direction
{
    Up,      // 默认 = 0
    Down,    // 默认 = 1
    Left,    // 默认 = 2
    Right    // 默认 = 3
}

默认规则:如果不指定值,第一个成员是 0,后面依次 +1

2.1 自定义数值

enum DayOfWeek
{
    Monday = 1,     // 指定从 1 开始
    Tuesday,        // = 2
    Wednesday,      // = 3
    Thursday,       // = 4
    Friday,         // = 5
    Saturday,       // = 6
    Sunday          // = 7
}

2.2 任意指定数值(可以跳、可以不连续)

enum HttpStatus
{
    OK = 200,
    BadRequest = 400,
    Unauthorized = 401,
    NotFound = 404,
    InternalServerError = 500
}

2.3 枚举的类型

枚举默认用 int 存储,也可以改成其他整数类型:

enum SmallEnum : byte     // 用 byte 存储(0~255)
{
    A, B, C
}

enum LargeEnum : long     // 用 long 存储
{
    One = 1,
    Million = 1000000L

}

支持的底层类型:bytesbyteshortushortintuintlongulong


三、使用枚举

3.1 声明变量和赋值

// 声明
Direction dir;

// 赋值
dir = Direction.Up;

// 声明+赋值
DayOfWeek today = DayOfWeek.Monday;

Console.WriteLine(today);  // 输出: Monday

3.2 比较枚举值

Direction dir = Direction.Left;

if (dir == Direction.Left)
{
    Console.WriteLine("向左走!");
}

// switch 是枚举的最佳搭档
switch (dir)
{
    case Direction.Up:
        Console.WriteLine("向上");
        break;
    case Direction.Down:
        Console.WriteLine("向下");
        break;
    case Direction.Left:
        Console.WriteLine("向左");
        break;
    case Direction.Right:
        Console.WriteLine("向右");
        break;
}

3.3 作为方法参数

static void Move(Direction direction, int steps)
{
    Console.WriteLine($"向{direction}方向移动{steps}步");
}

// 调用
Move(Direction.Up, 5);    // 输出: 向Up方向移动5步
Move(Direction.Left, 3);  // 输出: 向Left方向移动3步

四、枚举与整数互相转换

4.1 枚举 → 整数(强制转换)

DayOfWeek day = DayOfWeek.Friday;
int dayNumber = (int)day;

Console.WriteLine(dayNumber);  // 输出: 5

4.2 整数 → 枚举(强制转换)

int value = 3;
DayOfWeek day = (DayOfWeek)value;

Console.WriteLine(day);  // 输出: Thursday

注意:即使整数不在枚举定义的范围内,强制转换也不会报错!比如 (DayOfWeek)100 能执行,但结果是未定义行为。

4.3 判断整数是否为有效的枚举值

int value = 100;
bool isValid = Enum.IsDefined(typeof(DayOfWeek), value);

Console.WriteLine(isValid);  // 输出: False

五、枚举与字符串互相转换(常用!)

5.1 枚举 → 字符串

// 方式一:直接 ToString()
DayOfWeek day = DayOfWeek.Monday;
string dayStr = day.ToString();
Console.WriteLine(dayStr);  // 输出: Monday

// 方式二:Enum.GetName()
string name = Enum.GetName(typeof(DayOfWeek), 3);
Console.WriteLine(name);    // 输出: Thursday

5.2 字符串 → 枚举(Parse / TryParse)

// 方式一:Enum.Parse(可能抛异常!)
string input = "Friday";
DayOfWeek day = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), input);
Console.WriteLine(day);  // 输出: Friday

// 输入不存在的名字会抛异常:
// DayOfWeek bad = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), "NotADay");  // ❌ 抛异常!
// 方式二:Enum.TryParse(推荐!安全!)
Console.Write("请输入星期几: ");
string input = Console.ReadLine();  // 假设输入 "Monday"

if (Enum.TryParse<DayOfWeek>(input, out DayOfWeek day))
{
    Console.WriteLine($"转换成功: {day}");
}
else
{
    Console.WriteLine("无效的星期名,请输入 Monday~Sunday");
}

5.3 TryParse 忽略大小写

string input = "monday";  // 小写

// 默认是区分大小写的(根据版本不同行为可能不同,保险起见加参数)
if (Enum.TryParse(input, true, out DayOfWeek day))  // true = 忽略大小写
{
    Console.WriteLine($"转换成功: {day}");  // 输出: Monday
}

六、枚举的常用方法大全

6.1 获取所有枚举值

// 获取所有枚举值
DayOfWeek[] allDays = (DayOfWeek[])Enum.GetValues(typeof(DayOfWeek));

Console.WriteLine("一周的天数:");
foreach (DayOfWeek day in allDays)
{
    Console.WriteLine($"  {day} = {(int)day}");
}

输出:

一周的天数:
  Monday = 1
  Tuesday = 2
  Wednesday = 3
  Thursday = 4
  Friday = 5
  Saturday = 6
  Sunday = 7

6.2 获取所有枚举名称

string[] names = Enum.GetNames(typeof(DayOfWeek));

Console.WriteLine("所有名称:");
foreach (string name in names)
{
    Console.WriteLine(name);
}

输出:

所有名称:
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday

6.3 判断值是否合法

int userInput = 10;
if (Enum.IsDefined(typeof(DayOfWeek), userInput))
{
    Console.WriteLine($"{(DayOfWeek)userInput} 是有效的");
}
else
{
    Console.WriteLine($"{userInput} 不是有效的星期值");  // 输出这句
}

6.4 获取枚举的底层类型

Type underlyingType = Enum.GetUnderlyingType(typeof(DayOfWeek));
Console.WriteLine(underlyingType);  // 输出: System.Int32

七、完整示例:订单状态管理系统

using System;

// 定义订单状态
enum OrderStatus
{
    Pending = 1,       // 待处理
    Confirmed,         // 已确认 = 2
    Shipping,          // 配送中 = 3
    Delivered,         // 已送达 = 4
    Cancelled = -1     // 已取消 = -1(可以负值)
}

class Program
{
    // 处理方法:根据状态显示不同信息
    static void ShowOrderInfo(OrderStatus status, string orderId)
    {
        Console.Write($"订单 {orderId} 状态: {status}");

        switch (status)
        {
            case OrderStatus.Pending:
                Console.Write(" → 请尽快处理");
                break;
            case OrderStatus.Confirmed:
                Console.Write(" → 准备发货中");
                break;
            case OrderStatus.Shipping:
                Console.Write(" → 正在配送");
                break;
            case OrderStatus.Delivered:
                Console.Write(" → 已签收");
                break;
            case OrderStatus.Cancelled:
                Console.Write(" → 订单已取消");
                break;
        }
        Console.WriteLine();
    }

    // 返回所有状态的中文说明
    static Dictionary<OrderStatus, string> GetStatusDescriptions()
    {
        var dict = new Dictionary<OrderStatus, string>();
        foreach (OrderStatus s in Enum.GetValues(typeof(OrderStatus)))
        {
            dict[s] = s switch
            {
                OrderStatus.Pending => "待处理",
                OrderStatus.Confirmed => "已确认",
                OrderStatus.Shipping => "配送中",
                OrderStatus.Delivered => "已送达",
                OrderStatus.Cancelled => "已取消",
                _ => "未知"
            };
        }
        return dict;
    }

    static void Main()
    {
        // 1. 显示所有状态
        Console.WriteLine("===== 所有订单状态 =====");
        var descriptions = GetStatusDescriptions();
        foreach (var kv in descriptions)
        {
            Console.WriteLine($"  {kv.Key} (值={(int)kv.Key}): {kv.Value}");
        }

        // 2. 模拟处理订单
        Console.WriteLine("\n===== 订单处理流程 =====");

        // 用户输入状态值
        Console.Write("请输入订单状态代码 (1-待处理,2-已确认,3-配送中,4-已送达,-1-取消): ");
        string input = Console.ReadLine();

        if (int.TryParse(input, out int statusCode)
            && Enum.IsDefined(typeof(OrderStatus), statusCode))
        {
            OrderStatus status = (OrderStatus)statusCode;
            ShowOrderInfo(status, "ORD-2024001");
        }
        else
        {
            Console.WriteLine("无效的状态代码!");
        }

        // 3. 演示字符串解析
        Console.WriteLine("\n===== 字符串解析 =====");
        string[] testInputs = { "Delivered", "Cancelled", "Unknown" };
        foreach (string test in testInputs)
        {
            if (Enum.TryParse(test, out OrderStatus result))
            {
                Console.WriteLine($"'{test}' 解析成功 → {result}");
            }
            else
            {
                Console.WriteLine($"'{test}' 解析失败!");
            }
        }
    }
}

运行示例:

===== 所有订单状态 =====
  Pending (值=1): 待处理
  Confirmed (值=2): 已确认
  Shipping (值=3): 配送中
  Delivered (值=4): 已送达
  Cancelled (值=-1): 已取消

===== 订单处理流程 =====
请输入订单状态代码: 3
订单 ORD-2024001 状态: Shipping → 正在配送

===== 字符串解析 =====
'Delivered' 解析成功 → Delivered
'Cancelled' 解析成功 → Cancelled
'Unknown' 解析失败!

八、高级特性:Flags 枚举(标志位枚举)

有时候一个东西可以同时拥有多个状态。比如文件权限:可读、可写、可执行可以同时存在。

8.1 定义 Flags 枚举

[Flags]  // 加这个特性!
enum FilePermission
{
    None   = 0,   // 无权限
    Read   = 1,   // 可读   (二进制: 001)
    Write  = 2,   // 可写   (二进制: 010)
    Execute = 4,  // 可执行 (二进制: 100)
    FullControl = Read | Write | Execute  // 全部权限 = 7
}

关键:每个值必须是 2 的幂次方(1、2、4、8、16...),这样可以用位运算组合。

8.2 使用 Flags 枚举

// 组合多个权限(用 | 运算)
FilePermission myPermission = FilePermission.Read | FilePermission.Write;

// 检查是否包含某个权限(用 HasFlag 或 & 运算)
if (myPermission.HasFlag(FilePermission.Read))
{
    Console.WriteLine("有读权限");
}

if ((myPermission & FilePermission.Execute) == 0)
{
    Console.WriteLine("无执行权限");
}

// 添加权限
myPermission |= FilePermission.Execute;
Console.WriteLine($"添加执行权限后: {myPermission}"); // Read, Write, Execute

// 移除权限
myPermission &= ~FilePermission.Write;
Console.WriteLine($"移除写权限后: {myPermission}"); // Read, Execute

输出:

有读权限
无执行权限
添加执行权限后: Read, Write, Execute
移除写权限后: Read, Execute

8.3 完整示例:权限检查系统

using System;

[Flags]
enum Permission
{
    None        = 0,
    Create      = 1 << 0,  // 1    (这种写法更专业)
    Read        = 1 << 1,  // 2
    Update      = 1 << 2,  // 4
    Delete      = 1 << 3,  // 8
    Admin       = Create | Read | Update | Delete  // 15
}

class Program
{
    static void CheckPermission(Permission userPerm, Permission required)
    {
        Console.Write($"用户权限: {userPerm,-20} 需要: {required,-10}");
        if (userPerm.HasFlag(required))
        {
            Console.WriteLine(" ✅ 允许");
        }
        else
        {
            Console.WriteLine(" ❌ 拒绝");
        }
    }

    static void Main()
    {
        // 定义几个角色
        Permission guest = Permission.Read;
        Permission editor = Permission.Read | Permission.Update | Permission.Create;
        Permission admin = Permission.Admin;

        Console.WriteLine($"游客权限: {guest} (值={(int)guest})");
        Console.WriteLine($"编辑权限: {editor} (值={(int)editor})");
        Console.WriteLine($"管理员权限: {admin} (值={(int)admin})");
        Console.WriteLine();

        // 检查各角色能否删除
        CheckPermission(guest, Permission.Delete);
        CheckPermission(editor, Permission.Delete);
        CheckPermission(admin, Permission.Delete);

        Console.WriteLine();
        // 检查各角色能否读取
        CheckPermission(guest, Permission.Read);
        CheckPermission(editor, Permission.Read);
        CheckPermission(admin, Permission.Read);
    }
}

输出:

游客权限: Read (值=2)
编辑权限: Create, Read, Update (值=7)
管理员权限: Admin (值=15)

用户权限: Read                 需要: Delete     ❌ 拒绝
用户权限: Create, Read, Update 需要: Delete     ❌ 拒绝
用户权限: Admin                需要: Delete     ✅ 允许

用户权限: Read                 需要: Read       ✅ 允许
用户权限: Create, Read, Update 需要: Read       ✅ 允许
用户权限: Admin                需要: Read       ✅ 允许

九、枚举的常用技巧

9.1 给枚举加描述信息(Description 特性)

using System.ComponentModel;

enum OrderStatus
{
    [Description("待处理")]
    Pending = 1,

    [Description("已确认")]
    Confirmed,

    [Description("配送中")]
    Shipping,

    [Description("已送达")]
    Delivered,

    [Description("已取消")]
    Cancelled = -1
}

// 扩展方法:获取描述文字
static class EnumExtensions
{
    public static string GetDescription(this Enum value)
    {
        var field = value.GetType().GetField(value.ToString());
        var attr = (DescriptionAttribute)Attribute.GetCustomAttribute(
            field, typeof(DescriptionAttribute));
        return attr?.Description ?? value.ToString();
    }
}

// 使用
OrderStatus status = OrderStatus.Shipping;
Console.WriteLine(status.GetDescription());  // 输出: 配送中

9.2 枚举遍历并显示名称和值

Console.WriteLine("{0,-20} {1,-10}", "名称", "值");
Console.WriteLine(new string('-', 30));

foreach (OrderStatus status in Enum.GetValues(typeof(OrderStatus)))
{
    Console.WriteLine("{0,-20} {1,-10}", status, (int)status);
}

输出:

名称                   值
------------------------------
Pending               1
Confirmed             2
Shipping              3
Delivered             4
Cancelled             -1

9.3 从用户输入安全地获取枚举值

static OrderStatus? GetOrderStatusFromUser()
{
    Console.WriteLine("请选择订单状态:");

    // 列出所有可用选项
    foreach (OrderStatus status in Enum.GetValues(typeof(OrderStatus)))
    {
        Console.WriteLine($"  {(int)status} - {status}");
    }

    Console.Write("请输入数字: ");
    string input = Console.ReadLine();

    if (int.TryParse(input, out int code) && Enum.IsDefined(typeof(OrderStatus), code))
    {
        return (OrderStatus)code;
    }

    Console.WriteLine("无效输入!");
    return null;
}

十、常见易错点(避坑指南)

坑1:0 值建议定义

// ❌ 不推荐:没有 0 值
enum Status { Active = 1, Inactive = 2 }

// 未初始化的 Status 变量默认是 0,但 0 不是有效值!
Status s = new Status();
Console.WriteLine(s);  // 输出: 0 ← 一个不存在于定义中的值!

// ✅ 推荐:始终定义 0 值
enum Status
{
    Unknown = 0,   // 明确的默认值
    Active = 1,
    Inactive = 2
}

坑2:Parse 不存在的名字会抛异常

// ❌ 危险
string input = "Hackday";
DayOfWeek day = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), input);  // 抛异常!

// ✅ 安全
if (Enum.TryParse(input, out DayOfWeek safeDay))
{
    Console.WriteLine($"解析成功: {safeDay}");
}
else
{
    Console.WriteLine("解析失败");
}

坑3:强制转换不检查合法性

DayOfWeek day = (DayOfWeek)999;  // 不会报错!
Console.WriteLine(day);  // 输出: 999 ← 诡异的值

// 正确的做法:先检查
int value = 999;
if (Enum.IsDefined(typeof(DayOfWeek), value))
{
    DayOfWeek safe = (DayOfWeek)value;
    Console.WriteLine(safe);
}
else
{
    Console.WriteLine("无效值");
}

十一、总结

常用方法速查表

操作 代码
定义 enum Name { A, B, C }
赋值 Name x = Name.A;
转整数 int n = (int)x;
转枚举 Name x = (Name)2;
转字符串 x.ToString()
字符串转枚举(安全) Enum.TryParse("A", out Name x)
获取所有值 Enum.GetValues(typeof(Name))
获取所有名称 Enum.GetNames(typeof(Name))
检查是否合法 Enum.IsDefined(typeof(Name), value)
Flags 标志位 [Flags] + 2的幂次方值
组合权限 perm = A | B
检查权限 perm.HasFlag(A)
移除权限 perm &= ~A

记忆口诀

场景 口诀
定义 enum 起名花括号,成员逗号分隔开
默认值 不写值从 0 起,后面自动加上 1
声明 类型名加点来取值,比如 Status.Active
转整数 前面加个括号强转,(int)myEnum
转字符串 直接 .ToString() 就行
字符串转回来 Enum.TryParse 最安全
Flags [Flags],值用 1,2,4,8,组合用 |

一句话总结:枚举就是把"魔法数字"变成"有名字的选项",让代码更好读、更不容易出错。最常用的套路是:用 switch 处理不同枚举值,用 Enum.TryParse 从字符串安全转换,用 HasFlag 检查 Flags 组合枚举。

0
博主关闭了当前页面的评论