C#基础 枚举 Enumeration从基础到进阶

news/2025/1/14 15:26:22 标签: c#, .NET, 枚举

目录

  • 一、入门篇:枚举基础
  • 二、深入底层篇:存储、值与继承限制
  • 三、进阶使用篇
  • 四、总结
  • 参考文档

一、入门篇:枚举基础

1. 枚举类型的定义

枚举(Enumeration)由一组命名常量(称为枚举器)组成,用于建立一系列相互关联的整数常量,通过为这些常量分配描述性名称来增强代码的可读性和可管理性。

  • 语法enum 枚举名 { 成员名称 = 整数值, 其他成员名称, ... }
  • 要点
    • 枚举成员之间用","隔开。
    • 枚举类型可以继承 bytesbyteshortushortintuintlongulong 类型。
    • 第一个枚举成员如果没有显式设定值,则默认值为0,后续成员依次加1。
    • 指定值:也可以为枚举成员指定特定的值。
      • 如果枚举的第一个成员没有显式赋值,则其默认值为0,后续成员依次递增。
      • 如果后续某个成员显式赋值,则从这个成员开始,后续成员的值在其基础上依次递增。
c#">// 定义一个名为Weekday的枚举类型,继承自int类型
enum Weekday : int {
  // 枚举成员之间用逗号隔开
  Monday = 1, // 显式设定值为1
  Tuesday,    // 没有显式设定值,自动加1,值为2
  Wednesday,  // 自动加1,值为3
  Thursday,   // 自动加1,值为4
  Friday,     // 自动加1,值为5
  Saturday,   // 自动加1,值为6
  Sunday      // 自动加1,值为7
}
enum StatusCode { Success = 200, NotFound = 404, InternalServerError = 500 }

// 如果枚举的第一个成员没有显式赋值,则其默认值为0,后续成员依次递增
enum DefaultEnum1 { First, Second, Third }
// 如果后续某个成员显式赋值,则从这个成员开始,后续成员的值在其基础上依次递增
enum DefaultEnum2 { First, Second = 10, Third }

Console.WriteLine($"DefaultEnum1.First的值:{(int)DefaultEnum1.First}");   // 0
Console.WriteLine($"DefaultEnum1.Second的值:{(int)DefaultEnum1.Second}"); // 1
Console.WriteLine($"DefaultEnum1.Third的值:{(int)DefaultEnum1.Third}");   // 2

Console.WriteLine($"DefaultEnum2.First的值:{(int)DefaultEnum2.First}");   // 0
Console.WriteLine($"DefaultEnum2.Second的值:{(int)DefaultEnum2.Second}"); // 10
Console.WriteLine($"DefaultEnum2.Third的值:{(int)DefaultEnum2.Third}");   // 11

2. 枚举类型的优点

  • 资源占用少枚举是在栈中建立的数值类型,相比堆中建立的数据类型,运行程序时需要更少的处理器和内存资源,有助于提高程序的运行效率,降低系统资源的消耗。
  • 性能高枚举类型实质上是整数类型的别名,具有与整数相同的性能特征,这意味着在进行各种运算和操作时,枚举类型能够像整数一样快速高效地执行,不会因类型转换等因素而产生额外的性能开销。
  • 代码可读性强枚举类型为处理一组相关的常数提供了一种清晰的方式,通过为常数赋予具有明确意义的名称,使得代码更加直观易懂,其他开发者阅读代码时能够更快地理解代码的意图和逻辑,便于后续的代码维护和扩展。
  • 类型安全:使用枚举类型可以确保在代码中处理的常数值是预定义好的枚举成员之一,避免了使用普通的整数常量时可能出现的错误赋值或传递非法值的情况,从而提高了代码的健壮性和可靠性,减少潜在的运行时错误。

3. 枚举的基本使用

3.1 枚举的类型转换

  • 获取枚举成员的整数值(int)枚举.成员
    c#">Weekday today = Weekday.Monday;
    int dayValue = (int)today; // dayValue的值是0
    
    int value = 4;
    Weekday day = (Weekday)value; // day的值是Weekday.Thursday
    
  • 字符串与枚举的相互转换
    • 获取枚举成员的字符串值枚举.成员.ToString()Enum.GetName(typeof(枚举名), 枚举值)
    • 字符串转枚举Enum.Parse(typeof(枚举名), 字符串值)(枚举名)Enum.Parse(typeof(枚举名), 字符串值, true)(忽略大小写)。
    c#">// 获取枚举成员的字符串值
    Weekday day = Weekday.Friday;
    string dayName = day.ToString(); // 使用ToString()方法
    Console.WriteLine("枚举成员的字符串值:" + dayName);
    
    string dayName2 = Enum.GetName(typeof(Weekday), day); // 使用Enum.GetName()方法
    Console.WriteLine("枚举成员的字符串值:" + dayName2);
    
    // 忽略大小写字符串转枚举
    string dayStringIgnoreCase = "saturday";
    Weekday parsedDayIgnoreCase =
        (Weekday)Enum.Parse(typeof(Weekday), dayStringIgnoreCase, true);
    Console.WriteLine("忽略大小写字符串转枚举:" + parsedDayIgnoreCase);
    
    

3.2 遍历枚举成员

使用 Enum.GetValues(typeof(枚举名)) 获取枚举的所有成员。

c#">// 遍历枚举成员
Console.WriteLine("枚举的所有成员:");
foreach (Weekday weekday in Enum.GetValues(typeof(Weekday))) {
  Console.WriteLine(weekday);
}

3.3 判断枚举值是否有效

使用 Enum.IsDefined(typeof(枚举名), 值)

c#">// 判断枚举值是否有效
int validValue = 5;    // 对应Weekday.Friday
int invalidValue = 10; // 无效的枚举值
Console.WriteLine("枚举值" + validValue + "是否有效:" +
                  Enum.IsDefined(typeof(Weekday), validValue));
Console.WriteLine("枚举值" + invalidValue + "是否有效:" +
                  Enum.IsDefined(typeof(Weekday), invalidValue));

3.4枚举的比较

  • 比较枚举:可以直接使用比较运算符(如==!=<>等)比较两个枚举值的大小,比较的是枚举成员的整数值。
  • 比较枚举成员名称:如果需要比较枚举成员的名称字符串,可以先将枚举值转换为字符串,然后使用字符串的比较方法(如String.EqualsString.Compare等)进行比较。
c#">// 定义一个枚举
enum Sex { 男 = 1, 女 = 2 } 
// 比较枚举值
Sex sex1 = Sex.男;
Sex sex2 = Sex.女;
Console.WriteLine(sex1 == sex2); // false
Console.WriteLine(sex1 != sex2); // true
Console.WriteLine(sex1 > sex2);  // false
Console.WriteLine(sex1 < sex2);  // true

// 比较枚举成员名称
string sex1Name = sex1.ToString();
string sex2Name = sex2.ToString();
Console.WriteLine(String.Equals(sex1Name, sex2Name));       // false
Console.WriteLine(String.Compare(sex1Name, sex2Name) == 0); // false
Console.WriteLine(String.Compare(sex1Name, sex2Name) < 0);  // true
Console.WriteLine(String.Compare(sex1Name, sex2Name) > 0);  // false

4. 枚举的设计规范

  • 命名:简单枚举的命名采用单数形式,不要使用 EnumFlagFlags 作为枚举类型的后缀。
  • 零值枚举元素:考虑显式定义零值枚举元素,以防止未初始化的枚举值导致的意外行为。
  • 返回空实例:方法或属性返回枚举时,宁可返回一个空实例(如 Enum.GetValues(typeof(枚举名))[0])也不要返回空值(null),以提高代码的健壮性。

二、深入底层篇:存储、值与继承限制

1. 枚举的存储与表示

  • 枚举在底层是通过整数来存储的,每个枚举成员对应一个整数值。
  • 枚举类型的变量在内存中占用的空间取决于其基础类型(如 intbyte 等)。
    • 为什么要指定枚举值的底层类型

      指定枚举值的底层类型的目的是为了节省内存空间,或者适应特定的数据源。不同的整数类型占用的内存空间和取值范围不同,如下表所示:

      类型内存空间取值范围
      byte1 字节0 到 255
      sbyte1 字节-128 到 127
      short2 字节-32768 到 32767
      ushort2 字节0 到 65535
      int4 字节-2147483648 到 2147483647
      uint4 字节0 到 4294967295
      long8 字节-9223372036854775808 到 9223372036854775807
      ulong8 字节0 到 18446744073709551615

2. 枚举底层类型选择原则

  1. 根据成员数量和值大小选择:当枚举值的成员数量较少且数值较小时,应优先选用较小的整数类型作为底层类型,以节省内存空间并提升性能。

    例如,若枚举值仅有四个成员,可选用 byte 作为底层类型,相较于使用 int,可节省 3 个字节的内存空间。

  2. 依据数据源特性选择:若枚举值需与特定数据源(如数据库、文件或网络)相适配,则应选用与数据源相同或兼容的整数类型作为底层类型,以避免类型转换带来的错误或额外开销。

    例如,当数据源为 16 位无符号整数时,宜选用 ushort 作为底层类型,而非 int,这样可确保数据的一致性和准确性。

3. 枚举的继承与限制

  • 枚举不能继承自除基本整数类型以外的其他类型。
  • 枚举成员的值必须在其基础类型的范围内。
c#">using System;
using System.Runtime.InteropServices;

// 获取枚举类型的大小
int WeekdayenumSize = sizeof(Weekday);
Console.WriteLine("Weekday枚举类型占用的内存空间大小:" + WeekdayenumSize + " 字节");

// 定义一个继承自byte类型的枚举
enum Color : byte { Red, Green, Blue }
// 获取枚举类型的大小
int enumSize = sizeof(Color);
Console.WriteLine("枚举类型占用的内存空间大小:" + enumSize + " 字节");

// 获取byte类型的大小
int byteSize = sizeof(byte);
Console.WriteLine("byte类型占用的内存空间大小:" + byteSize + " 字节");

// 比较枚举类型和byte类型占用的内存空间大小
if (enumSize == byteSize) {
  Console.WriteLine("继承自byte类型的枚举占用的内存空间与byte类型相同");
} else {
  Console.WriteLine("继承自byte类型的枚举占用的内存空间与byte类型不同");
}
c#">Weekday枚举类型占用的内存空间大小:4 字节
枚举类型占用的内存空间大小:1 字节
byte类型占用的内存空间大小:1 字节
继承自byte类型的枚举占用的内存空间与byte类型相同

三、进阶使用篇

1. 标志 (Flags )枚举位运算

PS:可查看我的另一篇文章查看关于位运算更多内容。

位运算始末 |详解、特性思考、示例、运算巧思、相关算法、实际应用 (以C#为例)_c#归零-CSDN博客

  • 定义:通过给枚举加上 [Flags] 特性,使其能够表示多个枚举值的组合。
  • 赋值枚举成员的值通常定义为2的幂(如1, 2, 4, 8等),以便进行位运算。
  • 使用
    • 组合枚举值:使用位或运算符 |
    • 检查是否包含某个枚举值:使用位与运算符 & 和比较运算符 ==
    • 去除某个枚举值:使用位异或运算符 ^
c#">// 定义一个简单的枚举
public enum Sex { 女 = 0, 男 = 1 }

// 定义一个标志枚举
[Flags]
public enum Role {
  未分配 = 0,
  删除数据 = 1,
  修改数据 = 2,
  新增数据 = 4,
  查看数据 = 8
}

// 获取枚举成员的字符串值和整数值
string sexString = Sex.男.ToString();
int sexValue = (int)Sex.男;

// 组合枚举值
Role userRole = Role.删除数据 | Role.修改数据;

// 检查是否包含某个枚举值
bool hasDeletePermission = (userRole & Role.删除数据) == Role.删除数据;

// 输出结果
Console.WriteLine($"Sex: {sexString}, Value: {sexValue}");
Console.WriteLine($"User Role: {userRole}, Has Delete Permission: {hasDeletePermission}");

c#">Sex: 男, Value: 1
User Role: 删除数据, 修改数据, Has Delete Permission: True

2. 枚举的泛型方法

可以编写泛型方法来处理不同类型的枚举。例如:

c#">public static T GetEnumValue<T>(int value) where T : struct, Enum
{
    return (T)Enum.ToObject(typeof(T), value);
}

Weekday day = GetEnumValue<Weekday>(3); // day的值是Weekday.Wednesday

在这个例子中,GetEnumValue方法接受一个整数值和一个枚举类型作为参数,返回对应的枚举值。通过泛型方法,可以处理不同类型的枚举,提高代码的复用性.

3. 枚举的自定义属性

可以为枚举成员添加自定义属性,以存储额外的信息。这可以通过扩展方法来实现。

可以为枚举成员添加属性,以便存储额外的信息。例如:

c#">enum ErrorCode
{
    [Description("无效输入")]
    InvalidInput,
    [Description("超时")]
    Timeout,
    [Description("连接失败")]
    ConnectionFailed
}

在这个例子中,为ErrorCode枚举的每个成员添加了Description属性,用于存储错误代码的描述信息。可以通过反射来获取这些属性的值,例如:

c#">ErrorCode error = ErrorCode.InvalidInput;
var description = typeof(ErrorCode)
    .GetField(error.ToString())
    .GetCustomAttribute<DescriptionAttribute>()
    ?.Description;

Console.WriteLine(description); // 输出: 无效输入

4. 枚举的扩展方法

可以为枚举类型定义扩展方法,以便扩展枚举的功能。例如:

c#">public static class WeekdayExtensions
{
    public static bool IsWeekend(this Weekday day)
    {
        return day == Weekday.Saturday || day == Weekday.Sunday;
    }
}

Weekday today = Weekday.Saturday;
bool isWeekend = today.IsWeekend(); // true

在这个例子中,为Weekday枚举定义了一个扩展方法IsWeekend,用于判断一个工作日是否是周末.

四、总结

  1. 枚举基础
    • 枚举定义:一种数据类型,定义一组命名常量,提升代码可读性和可维护性。
    • 语法要点:enum 枚举名 { 成员名称 = 整数值, 其他成员名称, ... },成员间逗号隔开,可继承多种整数类型。
    • 枚举优点:在栈中建立,占用资源少;增强代码可读性和可维护性。
    • 基本使用:包括类型转换(获取整数值、字符串值,字符串转枚举)、遍历成员(Enum.GetValues(typeof(枚举名)))、判断有效性(Enum.IsDefined(typeof(枚举名), 值))。
    • 设计规范:命名单数形式;定义零值枚举元素;返回空实例非空值。
  2. 枚举的存储、值与继承限制
    • 存储表示:底层整数存储,变量占用空间依基础类型定。指定底层类型可节省内存或适配数据源。
    • 继承限制:不能继承除基本整数类型外的其他类型,成员值须在基础类型范围内。
  3. 枚举的进阶使用
    • 比较:可直接比较枚举值大小,也可比较成员名称字符串。
    • 标志枚举与位运算:[Flags]特性定义,成员值为2的幂,可组合、检查和去除枚举值。
    • 泛型方法:编写泛型方法处理不同类型枚举,提高代码复用性。
    • 自定义属性:为成员添加属性存储额外信息,可通过反射获取。
    • 扩展方法:定义扩展方法扩展枚举功能,如判断工作日是否为周末。

参考文档

  1. 在 C# 中实现枚举的最佳使用_c# 枚举-CSDN博客
  2. 枚举类型 - C# reference | Microsoft Learn

http://www.niftyadmin.cn/n/5822948.html

相关文章

2025华数杯国际赛A题完整论文讲解(含每一问python代码+数据+可视化图)

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了2025“华数杯”国际大学生数学建模竞赛A题Can He Swim Faster的完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成品论文…

Blender布料模拟动画制作

用blender做一个简单的布料模拟玩玩 建立一个plant 进入编辑模式 按A选中所有面&#xff0c;右键&#xff0c;进入曲面细分 将模型切为48&#xff08;当然越大效果越好&#xff0c;不过计算压力会越大&#xff09; 添加布料模拟&#xff08;Cloth&#xff09; 把Self collisio…

vue3+ts的<img :src=““ >写法

vue3ts的<img :src"" >写法<img :src"datasetImage" alt"数据分布示意图" /><script setup lang"ts">const datasetImage ref();datasetImage.value new URL(../../../assets/images/login-background.jpg, impo…

【股票数据API接口05】如何获取股票最新分时MA数据之Python、Java等多种主流语言实例代码演示通过股票数据接口获取数据

​ 如今&#xff0c;量化分析在股市领域风靡一时&#xff0c;其核心要素在于数据&#xff0c;获取股票数据&#xff0c;是踏上量化分析之路的第一步。你可以选择亲手编写爬虫来抓取&#xff0c;但更便捷的方式&#xff0c;莫过于利用专业的股票数据API接口。自编爬虫虽零成本&a…

从 Conda 到 Pip-tools:Python 依赖管理全景探索20250113

从 Conda 到 Pip-tools&#xff1a;Python 依赖管理全景探索 引言 在 Python 开发中&#xff0c;依赖管理是一个"常见但复杂"的问题&#xff1a;一次简单的版本冲突可能让团队调试数小时&#xff1b;一次不受控的依赖升级可能让生产环境瘫痪。随着项目规模的增加和…

python bs4 selenium 查找a href=javascript:();的实际点击事件和url

在使用 BeautifulSoup 和 Selenium 时&#xff0c;处理 href"javascript:;" 的链接需要一些额外的步骤&#xff0c;因为这些链接不直接指向一个 URL&#xff0c;而是通过 JavaScript 代码来执行某些操作。这可能包括导航到另一个页面、触发模态窗口、显示/隐藏内容等…

使用gtsam添加OrientedPlane3Factor平面约束因子

在基于地面约束的SLAM优化中&#xff0c;已知的地面信息&#xff08;如 plan.pcd 文件中的地面模型&#xff09;可以用作一个先验约束&#xff0c;以帮助优化位姿估计。具体而言&#xff0c;这个过程涉及将地面模型和每个帧的位姿结合&#xff0c;以创建一个因子模型&#xff0…

excel仅复制可见单元格,仅复制筛选后内容

背景 我们经常需要将内容分给不同的人&#xff0c;做完后需要合并 遇到情况如下 那是因为直接选择了整列&#xff0c;当然不可以了。 下面提供几种方法&#xff0c;应该都可以 直接选中要复制区域然后复制&#xff0c;不要选中最上面的列alt;选中可见单元格正常复制&#xff…