细说C#中的枚举:转换、标志和属性

枚举是 C# 中最有意思的一部分,大部分开发人员只了解其中的一小部分,甚至网上绝大多数的教程也只讲解了枚举的一部分。那么,我将通过这篇文章向大家具体讲解一下枚举的知识。我将从大家都了解的部分开始讲解,然后再讲解大家所不知道的或者了解很少的部分。

零、基础知识

枚举是由开发人员声明的一种 值类型 ,它在编译时就声明了一种 具名常量值 。使用枚举可以使我们的代码简单易读,我们先来看一下两个代码段:

// 代码段 1
void Method(int country)
{
 switch (country)
 {
  case 0:
   // more code
   break;
  case 1:
   // more code
   break;
  case 2:
   // more code
   break;
  case 3:
   // more code
   break;
  default:
   // more code
   break;
 }
} 

// 代码段 2
void Method(Country country)
{
 switch (country)
 {
  case Country.CN:
   // more code
   break;
  case Country.JP:
   // more code
   break;
  case Country.UK:
   // more code
   break;
  case Country.USA:
   // more code
   break;
  default:
   // more code
   break;
 }
} 

从上面的两个代码段我们可以看到两者有明显的区别。第一段代码中的 case 值我们几乎完全不知道代表了什么是什么意思,但是第二段代码我们使用了枚举,通过 case 值马上就可以知道所要表达的意思。同样利用枚举值替代布尔值也可以改善代码的可读性,例如我们要开发控制台灯打开关闭的程序,代码可以这么写 LightOperating(True) ,但是这种代码我们无法看出具体要干什么,现在我们将代码改动一下 LightOperating(Light.On) 。经过修改代码就很容易看出所要表达的意思。

1.枚举定义与取值 定义枚举有两种方式,分别是普通方式和自定义方式。不管使用哪种方式都需要用的关键字 enum 来标识这个类型为枚举类型,并且枚举值都是作为整数常量来实现的。下面我们就来看一下这两种方式怎么定义枚举的。普通方式是我们经常用到的,也是默认的方式。这种方式很简单,代码如下:

enum Country
{
 CN,
 UK,
 JP,
 USA
} 

在上面的代码段中我们定义了一个国家枚举,第一个枚举值对应的整数常量是 0 ,第二个枚举值对应的整数常量是 1 ,以此类推后面的枚举值分别对应的整数常量是 2 和 3 。但是在部分情况下我们需要自定义枚举值对应的整数常量,这个时候我们就需要用到自定义的方式。自定义方式又称为为枚举值显式赋值,它的方法如下所示:

enum Country
 {
  CN = 3,
  UK,
  JP = 70,
  USA = 67
 } 

我们在代码中将第一个枚举值对应的整数常量设置为了 3 ,这时第二个枚举值的整数常量就不是 1 了,而是 4 ,因为当枚举值没有显示赋值时,将会按照上一个枚举值对应的整数值加 1 来作为自己本身对应的整数值。最后两个枚举值因为显式赋值了因此对应的整数值就是所赋值的数值。 枚举取值也很简单,只需要 枚举名.枚举值 即可,例如 Country.UK 。

Tip:这里我提几点建议:

  • 枚举值的名称不应包含枚举名称;
  • 枚举名称应以单数的形式出现(除了属性)。

2.枚举的类型 到目前为止我们定义枚举类型使用的基础类型 int 类型,但是枚举不仅仅可以使用 int 类型,还可以使用除了 char 类型之外的所有基础类型。我们可以使用继承语法来指定其他类型。

enum Country:short
 {
  CN = 3,
  UK,
  JP = 70,
  USA = 67
 } 

上面代码中我们显式定义了枚举所使用的基础类型为 short 。这里虽然使用了继承语法但是并没有建立继承关系,所有的枚举基类都是 System.Enum ,这些类都是密封类,无法从现有的枚举类型派生出新的成员。 对于枚举类型的变量,值不限于声明中命名的值,因此值能转换成基础类型,那么就能转换为枚举类型。之所以这么设计是因在以后的 API 中有很大的可能在不破换老版本的同时为枚举添加新的值。但是这其中也存在一个缺陷,枚举允许在运行时分配未知的值,对于这一点我们在开发时需要考虑到。并且在后期向枚举中添加新的枚举值时应将其添加到所有枚举值的后面,或者显示指定枚举值对应的数值,这样才能避免因添加新值导致枚举类型中的枚举值对应的数值改变。

Tip:在开发中我们应该尽量使用 int 作为枚举的基础类型,除非因性能问题或互操作方面的考虑时才会考虑使用较小的类型。

一、枚举转换

枚举转换主要涉及到了枚举与枚举的转换、枚举与数字和字符串的转换。

1.枚举之间转换 首先我要说明的是在 C# 中不支持不同枚举数组之间的直接转换,所以如果想要实现不同枚举数组之间的转换我们可以利用 CLR 宽松的赋值兼容性这一特点来进行转换,需要转换的两个枚举必须具有相同的基础类型。同样,我们通过一个例子来看一下具体实现方法。

static void Main(string[] args)
 {
  CountryAllName[] can = (CountryAllName[])(Array)new Country[4];
 }
enum Country
 {
  CN,
  UK,
  JP,
  USA
 }
 enum CountryAllName
 {
  China,
  UnitedKingdom,
  Japan,
  UnitedStates
 } 

在使用这种方法时有可能会出现意外的错误或结果,并且相关开发规范中并没有说这种方式每次都起作用,因此我不建议这么使用,除非在一些极端场景中。

2.枚举和字符串之间转换 枚举转换为字符串可以直接使用 ToString() 方法, 枚举值 ToString 后会直接输出枚举值标识符的字符串形式,例如 Country.CN.ToString() 得到的结果是字符串 CN 。当然,你也可以利用 Enum.GetNames 和 Enum.GetName 方法来获取。下面我简单来讲解一下这两个方法的使用。

  • GetNames GetNames 方法需要传入一个枚举类型,返回值是一个字符串数组。例如需要获取到 Country 的第二个国家,那么就可以这么来写 Enum.GetNames(typeof(Country))[1] ,返回结果是 UK 。
  • GetName GetName 方法返回的是一个字符串,这个字符串就是需要获取的指定枚举值的字符串形式。同样我们获取第二个国家, Enum.GetName(typeof(Country),1) ,返回的值同样是 UK 。 字符串转换为枚举也很简单,同样用到了 Enum 基类的一个静态方法 Parse ,例如我们将 JP 转换为枚举 Country 的枚举值可以这么做 (Country)Enum.Parse(typeof(Country),"JP") 。这里有一点需要注意, TryParse 方法是在 .net 4.0 才出现的,因此如果要在 .net 4.0 以下版本中将字符串转换为枚举时,需要进行恰当的错误处理防止字符串不存在与枚举类型中的枚举值中。

Tip:字符串向枚举转换不可本地化,如果必须本地化,就必须是那些对上层用户不可见的消息。因此在实际开发中应该尽量避免枚举和字符串之间的转换。

3.枚举和数字之间转换 枚举转换为数字我们可以使用强转,例如 (int)Country.CN 返回结果是 0 。从数字转换为枚举我们有两种方法,一种是使用强转,另一种是使用 Enum 的静态方发 ToObject 。

  • 强转 强转就比较简单了, Country country = (Country)2
  • ToObject ToObject 方法需要传入枚举类型和需要转换的数字,例如 Country country = (Country)Enum.ToObject(typeof(Country),2)

4.注意 字符串转换为枚举和数字转换为枚举都必须先进行判断所要转换的值是否包含在枚举中,判断的方法也很简单只需要调用 Enum 的静态方法 IsDefined 即可,例如我要将 0 和 HK 转换为枚举,代码如下:

Type type = typeof(Country);
 if(Enum.IsDefined(type,0))
 {
  Enum.ToObject(type, 0);
 }
 if(Enum.IsDefined(type,"HK"))
 {
  Enum.Parse(typeof(Country), "HK");
 } 

上述代码中只有 0 会成功转换为枚举值 CN ,因为 0 所对应的枚举值是 CN ,而 HK 并没有在枚举中。

三、标志与属性

这一小节我们来讲解一下标志与属性,标志he和属性属于在开发中用的比较少,并且大部分程序员了解的也不多。

1.标志 在开发中有时我们希望能对枚举进行组合使用来表示复合值,那么这时我们就需要定义标志枚举了,标志枚举的名称为复数形式,代表了一个标志的集合。一般我们会使用按位或操作符链接枚举值,使用 HasFlags 方法或者按位与操作符来判断特定的位是否存在。比较经典的标志枚举是位于 System.IO 命名空间中的 FileAttributes 标志枚举,它列出了文件的所有属性,比如只读、隐藏、所在磁盘等等,它所包含的所有枚举值皆可相互组合,例如一个文件既是隐藏文件又是只读文件。定义标志枚举的方法如下:

[Flags]
enum WeekDays
 {
  Monday = 1,
  Tuesday = 2,
  Wednesday = 4,
  Thursday = 8,
  Friday = 16,
  Saturday = 32,
  Sunday = 64
 }

在上面的代码中你会发现一个规律,每个枚举值对应的整数值都是 2的n次方,这是为什么呢。在标志枚举中要求多个枚举值相互组合后的结果不能包含在标志枚举中,并且基于按位运算的特性可以很方便的使用位运算符来计算一个枚举值是否包含了另外一个枚举值,这在权限系统中相当有用。

2.属性 枚举值上同样也可以使用属性,例如我们需要打印输出枚举值的中文名,我们就可以通过属性的形式进行设置,首先我们需要定义一个属性:

public class EnumChineseAttribute : Attribute
 {
  private string m_strDescription;
  public EnumChineseAttribute(string chineseName)
  {
   m_strDescription = chineseName;
  } 

  public string Description
  {
   get { return m_strDescription; }
  }
 }
 enum Country
 {
  [EnumChinese("中国")]
  CN,
  [EnumChinese("英国")]
  UK,
  [EnumChinese("日本")]
  JP,
  [EnumChinese("美国")]
  USA
 }
 static void Main(string[] args)
 {
  Country country = Country.CN;
  FieldInfo fieldInfo = country.GetType().GetField("CN");
  object[] attribArray = fieldInfo.GetCustomAttributes(false);
  EnumChineseAttribute attrib = (EnumChineseAttribute)attribArray[0];
  Console.WriteLine(attrib.Description);
  Console.Read();
 } 

通过上面的代码我们就能获取到 CN 对应的中文名称了,这段代码并没有进行进一步优化,在实际项目中必须进行封装和优化。

四、小结

这篇文章主要讲解了枚举相关的知识,内容有点琐碎,但是在实际开发中还是比较实用的。文章中我所提到的要点和规定在实际开发中已经经过验证,各位读者可以直接拿来使用。

以上所述是小编给大家介绍的细说C#中的枚举:转换、标志和属性,希望对大家有所帮助!

(0)

相关推荐

  • C#日期格式强制转换方法(推荐)

    C#编写winform程序时,用到的,格式强转,存储到数据库,数据库连接那块就不写了 希望对大家有帮助,欢迎评论互相分享技术! //日期格式强制转化 string str1 = deStartDate.EditValue.ToString(); DateTime date; DateTime.TryParse(str1, out date); str1 = date.ToString("yyyy/MM/dd"); string str2 = deDueDate.EditValue.To

  • 理解C#中的枚举(简明易懂)

    一.在学习枚举之前,首先来听听枚举的优点. 1.枚举能够使代码更加清晰,它允许使用描述性的名称表示整数值. 2.枚举使代码更易于维护,有助于确保给变量指定合法的.期望的值. 3.枚举使代码更易输入. 二.枚举说明 1.简单枚举 ●枚举使用enum关键字来声明,与类同级.枚举本身可以有修饰符,但枚举的成员始终是公共的,不能有访问修饰符.枚举本身的修饰符仅能使用public和internal. ●枚举是值类型,隐式继承自System.Enum,不能手动修改.System.Enum本身是引用类型,继承

  • C# 字符串、数组和List的截取和转换实例

    如下所示: using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading; namespace ConsoleApp1 { class Program { /// <summary> /// 字符串,数组和List的截取,转换 /// </summary> /// <pa

  • C# 从枚举值获取对应的文本描述详解

    C# 从枚举值获取对应的文本描述详解 有时枚举值在显示时,需要显示枚举值对应的文本串.一种方案是在调用的地方使用switch或者if来判断枚举值,然后赋给不同的文本串,但这样一来,如果有较多的地方都用到的时候就会比较麻烦.当然有人说,这种情况下,可以针对这种枚举值封装一个方法,然后来调用.那如果有多个枚举类型都有这样的需求呢?有没有什么比较通用的解决办法?有的. 这里需要用到Description属性,给每个枚举值都赋上一个该属性,然后在该属性中赋上要描述的文本串.比如 #region YesN

  • C#实现获取枚举中元素个数的方法

    本文以一个简单实例讲述了C#实现获取枚举中元素个数的方法,对于C#学习来说是需要加以牢固掌握的技巧.分享给大家供大家参考之用.具体如下: 实现该功能可以使用枚举基类System.Enum的GetNames(Type t) 方法来获取某个枚举中的元素列表. 具体功能代码如下: enum EnumTest { type1, type2, } class Program { static void Main(string[] args) { EnumTest e = new EnumTest(); s

  • 细说C#中的枚举:转换、标志和属性

    枚举是 C# 中最有意思的一部分,大部分开发人员只了解其中的一小部分,甚至网上绝大多数的教程也只讲解了枚举的一部分.那么,我将通过这篇文章向大家具体讲解一下枚举的知识.我将从大家都了解的部分开始讲解,然后再讲解大家所不知道的或者了解很少的部分. 零.基础知识 枚举是由开发人员声明的一种 值类型 ,它在编译时就声明了一种 具名常量值 .使用枚举可以使我们的代码简单易读,我们先来看一下两个代码段: // 代码段 1 void Method(int country) { switch (country

  • mybatis中实现枚举自动转换方法详解

    前言 最近在工作中遇到一个问题,在设计数据库的时候,我们有时候会把表里的某个字段的值设置为数字或者为英文来表示他的一些特殊含义.就拿设置成数字来说,假如1对应是学生,2对应是教师,在Java里面定义成这样的枚举,但是一般使用mybatis查出来的话,我们想要让它自动装换成我们想要的枚举,不需要再手动根据数值去判断设置成我们想要的枚举.要是实现这样的效果,那么我们就要用到mybatis的BaseTypeHandler了. BaseTypeHandler介绍 让我们来看看要继承BaseTypeHan

  • java中的枚举类型详细介绍

    枚举中有values方法用于按照枚举定义的顺序生成一个数组,可以用来历遍.我们自定义的枚举类都是继承自java.lang.Enum,拥有一下实例中的功能: 复制代码 代码如下: //: enumerated/EnumClass.java // Capabilities of the Enum class import static net.mindview.util.Print.*; enum Shrubbery { GROUND, CRAWLING, HANGING } public clas

  • 深入理解C#中的枚举

    枚举类型是一种的值类型,它用于声明一组命名的常数. (1)枚举的声明:枚举声明用于声明新的枚举类型.访问修辞符 enum 枚举名:基础类型 复制代码 代码如下: {        枚举成员    } 基础类型必须能够表示该枚举中定义的所有枚举数值.枚举声明可以显式地声明 byte.sbyte.short.ushort.int.uint.long 或 ulong 类型作为对应的基础类型.没有显式地声明基础类型的枚举声明意味着所对应的基础类型是 int. (2)枚举成员枚举成员是该枚举类型的命名常数

  • 结合C++11的新特性来解析C++中的枚举与联合

    枚举 枚举是用户定义的类型,其中包含一组称为枚举器的命名的整型常数. 语法 // unscoped enum: enum [identifier] [: type] {enum-list}; // scoped enum: enum [class|struct] [identifier] [: type] {enum-list}; // Forward declaration of enumerations (C++11): enum A : int; // non-scoped enum mu

  • .NET中的枚举用法浅析

    本文简单分析了.NET中的枚举用法.分享给大家供大家参考.具体分析如下: 我理解的枚举就是编程中约定的一个"可选值":例如QQ的在线状态,分别有    在线,Q我吧,隐身,忙碌等等...我觉得这就是一个枚举. 1.普通枚举 1) 实例 复制代码 代码如下: public enum UserState {  QMe,  OnLine,  OffLine     //后面省略 } ps:像上边的枚举,可以单独写在一个CS文件中. 2)普通枚举的性质 1. 互斥性 2. 每一个值都有一个数值

  • Spring boot JPA实现分页和枚举转换代码示例

    1.实现分页 方法一:使用Pageable 使用Pageable作为入参传入Repository,返回值用Page<T>接收 UserRepository package com.kinglead.demo.dao; ​ import com.kinglead.demo.entity.User; import org.springframework.data.jpa.repository.JpaRepository; ​ public interface UserRepository exte

  • Spring IOC:CreateBean环节中的流程转换

    一 . 前言 此篇文章的目的 : 梳理Bean 的创建流程 , 便于后续查找问题点 梳理过程中的参数情况 , 减少Debug的需求 梳理整体家族体系 Bean 创建的几个触发场景 : BeanFactory 的 #getBean(...) 方法来请求某个实例对象的时候 使用 ApplicationContext 容器时 , 会在启动时立即注册部分 Bean 二 . 流程梳理 先来看一个很常见的图 , 来源于 @ topjava.cn/article/139- 同样的 , 这篇的流程整理也是按照此

  • SpringBoot在RequestBody中使用枚举参数案例详解

    前文说到 优雅的使用枚举参数 和 实现原理,本文继续说一下如何在 RequestBody 中优雅使用枚举. 本文先上实战,说一下如何实现.在 优雅的使用枚举参数 代码的基础上,我们继续实现. 确认需求 需求与前文类似,只不过这里需要是在 RequestBody 中使用.与前文不同的是,这种请求是通过 Http Body 的方式传输到后端,通常是 json 或 xml 格式,Spring 默认借助 Jackson 反序列化为对象. 同样的,我们需要在枚举中定义 int 类型的 id.String

  • 教你如何用好 Java 中的枚举

    目录 1.概览 2.自定义枚举方法 3.使用 == 比较枚举类型 4.在 switch 语句中使用枚举类型 6.EnumSet and EnumMap 6.1. EnumSet 6.2. EnumMap 7. 通过枚举实现一些设计模式 7.1 单例模式 7.2 策略模式 8. Java 8 与枚举 9. Enum 类型的 JSON 表现形式 10. 补充 1.概览 enum关键字在 java5 中引入,表示一种特殊类型的类,其总是继承java.lang.Enum类,更多内容可以自行查看其官方文档

随机推荐