一篇文章搞懂C#的枚举

什么是枚举?

我上周看到一个新入职的同时写switch语句时, 直接用case 1: ,case 2 ,直接用数字来写代码,后面跟一长排注释,态度很好,但没必要,我告诉他你应该使用枚举。使用枚举就应该像使用excel中使用填充柄功能一样自然,顺畅,因为你不会一个一个输入数字1、2、3·······10000,那样太累,也没有必要。

枚举是一种特殊的类,用于表示一组相关的常量。举个例子,假设我们要表示一周的七天,这七天是固定的,我们可以用枚举来表示,当然你也可以用字符串或者整数表示,但那种表示方式不太好,比如你可以用 1 表示星期一,但是一般人很难将星期一和数字1联系起来,但是我们如果用Monday来表示星期一,懂英语就知道这种表示方式。

为什么要使用枚举?

可读性:代码更容易读懂。例如,DayOfWeek.Monday1更容易理解。

减少错误:枚举一般会和判断语句结合使用,比如判断今天是否是星期五,我们用if(today == 5), 这种方式可以吗?可以,但是容易写错,但是我们用if(today == Friday),这就不容易写错,所见即所得,没有中间的转换过程,可以减少拼写错误或者使用错误的值。

维护方便:修改或者添加新的枚举值非常方便。假设我们事先约定好一些列数字,比如1代表只读,2代表只写,3代表读写,后来我们又出了一个,用4代表禁止读写,我们只需要在枚举中加一行代码就搞定,很方便。

如何定义枚举?

在C#中,可以使用enum关键字来定义枚举。比如,我们定义一个表示一周七天的枚举:

public enum DayOfWeek
{
    
    
    Sunday,//默认从0开始,并按定义文本顺序递增 1
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday
}

在上面的例子中,每个枚举值其实对应一个int整数值,默认从0开始。我们也可以手动指定每个枚举值对应的整数

public enum DayOfWeek
{
    
    
    Sunday = 1,
    Monday = 2,
    Tuesday = 3,
    Wednesday = 4,
    Thursday = 5,
    Friday = 6,
    Saturday = 7
}

如何使用枚举?

定义了枚举后,我们可以在代码中使用它。例如:

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

// 也可以通过整数转换枚举
int dayNumber = (int)DayOfWeek.Wednesday;
Console.WriteLine(dayNumber);  // 输出: 3

枚举的高级用法

枚举的位标志(Flags)

有时候我们需要一个变量能够表示多个状态。例如,一个文件可以同时具有读、写和执行权限。在这种情况下,可以使用枚举的位标志功能。首先,需要使用[Flags]特性来标识这个枚举,然后定义各个标志位。

//使用这个枚举时,可以进行位运算:
FileAccess access = FileAccess.Read | FileAccess.Write;

Console.WriteLine(access);  // 输出: ReadWrite

// 检查是否包含某个标志
bool canRead = (access & FileAccess.Read) == FileAccess.Read;
Console.WriteLine(canRead);  // 输出: True

bool canExecute = (access & FileAccess.Execute) == FileAccess.Execute;
Console.WriteLine(canExecute);  // 输出: False


[Flags]
public enum FileAccess
{
    
    
    None = 0,
    Read = 1,
    Write = 2,
    Execute = 4,
    ReadWrite = Read | Write,
    All = Read | Write | Execute
}

我们来看另外一个枚举位运算的代码

扫描二维码关注公众号,回复: 17469169 查看本文章
[Flags]
public enum Days
{
    
    
    None      = 0b_0000_0000,  // 0
    Monday    = 0b_0000_0001,  // 1
    Tuesday   = 0b_0000_0010,  // 2
    Wednesday = 0b_0000_0100,  // 4
    Thursday  = 0b_0000_1000,  // 8
    Friday    = 0b_0001_0000,  // 16
    Saturday  = 0b_0010_0000,  // 32
    Sunday    = 0b_0100_0000,  // 64
    Weekend   = Saturday | Sunday
}

public class FlagsEnumExample
{
    
    
    public static void Main()
    {
    
    
        Days meetingDays = Days.Monday | Days.Wednesday | Days.Friday;//星期一三五开会
        Console.WriteLine(meetingDays);
        // Output:
        // Monday, Wednesday, Friday

        Days workingFromHomeDays = Days.Thursday | Days.Friday;
        Console.WriteLine($"Join a meeting by phone on {
      
      meetingDays & workingFromHomeDays}");//开会日同时是工作日
        // Output:
        // Join a meeting by phone on Friday

        bool isMeetingOnTuesday = (meetingDays & Days.Tuesday) == Days.Tuesday;
        Console.WriteLine($"会议是否在星期三: {
      
      isMeetingOnTuesday}");
        // Output:
        // Is there a meeting on Tuesday: False

        var a = (Days)37;
        Console.WriteLine(a);
        // Output:
        // Monday, Wednesday, Saturday
    }
}

转换枚举和字符串

有时候,我们需要在枚举和字符串之间转换。可以使用Enum.ParseEnum.ToString方法。

// 枚举转字符串
DayOfWeek today = DayOfWeek.Friday;
string todayString = today.ToString();
Console.WriteLine(todayString);  // 输出: Friday

// 字符串转枚举
string dayString = "Sunday";
DayOfWeek day = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), dayString);
Console.WriteLine(day);  // 输出: Sunday

遍历枚举

可以使用Enum.GetValues方法来遍历枚举中的所有值。

foreach (DayOfWeek day in Enum.GetValues(typeof(DayOfWeek)))
{
    
    
    Console.WriteLine(day);
}

枚举在实际项目中的应用

在实际项目中,枚举可以用于许多场景,以下是一些常见的使用示例:

状态机

在状态机中,枚举可以用来表示各种状态。例如,表示订单的状态:

public enum OrderStatus
{
    
    
    Pending,   //悬挂
    Processed, //处理
    Shipped,   //运输
    Delivered, //交付
    Cancelled  //取消
}

在处理订单时,可以根据订单的不同状态执行不同的操作:

public void ProcessOrder(OrderStatus status)
{
    
    
    switch (status)
    {
    
    
        case OrderStatus.Pending:
            // 处理待处理订单
            break;
        case OrderStatus.Processed:
            // 处理已处理订单
            break;
        case OrderStatus.Shipped:
            // 处理已发货订单
            break;
        case OrderStatus.Delivered:
            // 处理已交付订单
            break;
        case OrderStatus.Cancelled:
            // 处理已取消订单
            break;
        default:
            throw new ArgumentOutOfRangeException(nameof(status), status, null);
    }
}
配置选项

枚举也可以用于表示配置选项。例如,一个日志系统可以有不同的日志级别:

public enum LogLevel
{
    
    
    Debug,   //调试
    Info,	 //信息
    Warning, //警告
    Error,   //错误
    Critical //严重错误
}

使用时可以根据不同的日志级别执行不同的操作:

枚举可以很好地表示配置选项。以日志系统为例,不同的日志级别可以用枚举来表示,这样代码更加清晰、可读性更高。以下是一个示例代码,展示了如何在C#中使用枚举表示日志级别:

using System;

namespace LoggingExample
{
    
    
    // 定义日志级别的枚举
    public enum LogLevel
    {
    
    
        Debug,
        Info,
        Warning,
        Error,
        Critical
    }

    // 日志类
    public class Logger
    {
    
    
        private LogLevel _currentLogLevel;

        // 构造函数,初始化日志级别
        public Logger(LogLevel logLevel)
        {
    
    
            _currentLogLevel = logLevel;
        }

        // 打印日志的方法
        public void Log(LogLevel level, string message)
        {
    
    
            if (level >= _currentLogLevel)
            {
    
    
                Console.WriteLine($"[{
      
      level}] - {
      
      message}");
            }
        }
    }

    class Program
    {
    
    
        static void Main(string[] args)
        {
    
    
            // 创建一个日志实例,并设置日志级别为Info
            Logger logger = new Logger(LogLevel.Info);

            // 测试日志输出
            logger.Log(LogLevel.Debug, "This is a debug message."); // 这条消息不会被输出
            logger.Log(LogLevel.Info, "This is an info message.");
            logger.Log(LogLevel.Warning, "This is a warning message.");
            logger.Log(LogLevel.Error, "This is an error message.");
            logger.Log(LogLevel.Critical, "This is a critical message.");
        }
    }
}

在这个示例中,我们定义了一个枚举 LogLevel,表示不同的日志级别。然后在 Logger 类中使用这个枚举来控制日志的输出。当日志级别高于或等于当前设置的日志级别时,消息将被输出到控制台。这种方式可以使日志系统的配置选项更加直观和易于维护。你可以根据需要扩展枚举,添加更多的日志级别或其他配置选项。

枚举的常见问题和解决方法

问题1:如何将枚举的描述信息显示给用户?

有时候,我们希望将枚举值的描述信息(而不是枚举值本身)显示给用户。可以使用[Description]特性和反射来实现。

using System;
using System.ComponentModel;
using System.Reflection;

public enum OrderStatus
{
    
    
    [Description("待处理")]
    Pending,
    [Description("已处理")]
    Processed,
    [Description("已发货")]
    Shipped,
    [Description("已交付")]
    Delivered,
    [Description("订单取消")]
    Cancelled
}

public static class EnumExtensions //扩张方法
{
    
    
    public static string GetDescription(this Enum value)
    {
    
    
        FieldInfo field = value.GetType().GetField(value.ToString());//获取字段
        DescriptionAttribute attribute = field.GetCustomAttribute<DescriptionAttribute>();//获取字段上的特性
        return attribute == null ? value.ToString() : attribute.Description;//放回特性的表述信息
    }
}

class Program
{
    
    
    static void Main()
    {
    
    
        OrderStatus status = OrderStatus.Shipped;
        Console.WriteLine(status.GetDescription());  // 输出: Order shipped
    }
}
问题2:如何确保枚举值唯一且连续?

在定义枚举时,有时需要确保枚举值唯一且连续。可以通过显式赋值来实现。

public enum ErrorCode
{
    
    
    None = 0,
    NotFound = 1,
    InvalidParameter = 2,
    Unauthorized = 3,
    InternalError = 4
}
问题3:如何将枚举值与数据库中的值对应?

在将枚举值存储到数据库或从数据库读取时,可以使用枚举的整数值或者字符串值。下面是一个示例,演示如何使用枚举值与数据库中的值进行映射。

public enum UserRole
{
    
    
    Admin = 1,
    User = 2,
    Guest = 3
}

// 保存到数据库时,将枚举转换为整数
int roleValue = (int)UserRole.Admin;

// 从数据库读取时,将整数转换为枚举
UserRole role = (UserRole)roleValue;
问题4:如何处理不存在的枚举值?

在某些情况下,可能会遇到不存在的枚举值。可以使用Enum.IsDefined方法来检查枚举值是否存在。

public static bool IsValidEnumValue<T>(int value) where T : Enum
{
    
    
    return Enum.IsDefined(typeof(T), value);
}

class Program
{
    
    
    static void Main()
    {
    
    
        int value = 5;
        bool isValid = IsValidEnumValue<DayOfWeek>(value);
        Console.WriteLine(isValid);  // 输出: False
    }
}
问题5: 使用枚举简化代码

在许多业务逻辑中,使用枚举可以大大简化代码。例如,在处理不同类型的付款方式时,可以使用枚举来表示不同的付款类型。

public enum PaymentMethod
{
    
    
    CreditCard,
    DebitCard,
    PayPal,
    BankTransfer
}

public void ProcessPayment(PaymentMethod method)
{
    
    
    switch (method)
    {
    
    
        case PaymentMethod.CreditCard:
            // 处理信用卡付款
            break;
        case PaymentMethod.DebitCard:
            // 处理借记卡付款
            break;
        case PaymentMethod.PayPal:
            // 处理PayPal付款
            break;
        case PaymentMethod.BankTransfer:
            // 处理银行转账付款
            break;
        default:
            throw new ArgumentOutOfRangeException(nameof(method), method, null);
    }
}
问题6: 枚举如何与自定义属性结合

可以将枚举与自定义属性结合使用,为枚举值附加更多信息。例如,为枚举值附加显示名称或颜色信息。

public class DisplayAttribute : Attribute
{
    
    
    public string Name {
    
     get; }
    public string Color {
    
     get; }

    public DisplayAttribute(string name, string color)
    {
    
    
        Name = name;
        Color = color;
    }
}

public enum TrafficLight
{
    
    
    [Display("Stop", "Red")]
    Red,
    [Display("Caution", "Yellow")]
    Yellow,
    [Display("Go", "Green")]
    Green
}

public static class EnumExtensions
{
    
    
    public static DisplayAttribute GetDisplayAttribute(this Enum value)
    {
    
    
        FieldInfo field = value.GetType().GetField(value.ToString());
        return field.GetCustomAttribute<DisplayAttribute>();
    }
}

class Program
{
    
    
    static void Main()
    {
    
    
        TrafficLight light = TrafficLight.Red;
        DisplayAttribute display = light.GetDisplayAttribute();
        Console.WriteLine($"{
      
      display.Name} - {
      
      display.Color}");  // 输出: Stop - Red
    }
}

总结

我们用人话总结一下: 在你编写源代码的时候, 千万不要直接在源文件中使用数字/字符/字符串 , 应该改为使用枚举, 这样做的好处是, 将来你修改代码时,只要在枚举中修改就行了, 而不用去整个代码中找哪里1 改成 2, 哪里2 又改成3,那样太费力了, 还有一个更大的好处是, 使用枚举可以让代码所见即所得, 其他人不需要问1代表什么,2 代表什么意思,这样在团队协作时,省的别人啥事都来问你。

猜你喜欢

转载自blog.csdn.net/weixin_45589116/article/details/140135782