一篇文章彻底弄明白C#中的枚举

热点 201 0
什么是枚举?

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

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

一篇文章彻底弄明白C#中的枚举

为什么要使用枚举?

可读性:代码更容易读懂。例如,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); // 输出: Truebool 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}

我们来看另外一个枚举位运算的代码,关于日程表的

[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 代表什么意思,这样在团队协作时,省的别人啥事都来问你。



标签: 枚举

抱歉,评论功能暂时关闭!