书接上回,今天继续和大家享一些关于枚举操作相关的常用扩展方法。
今天主要分享通过枚举值转换成枚举、枚举名称以及枚举描述相关实现。
我们首先修改一下上一篇定义用来测试的正常枚举,新增一个枚举项,代码如下:
01、根据枚举值转换成枚举
该方法接收枚举值作为参数,并转为对应枚举,转换失败则返回空。
枚举类Enum中自带了两种转换方法,其一上篇文章使用过即Parse,这个方法可以接收string或Type类型作为参数,其二为ToObject方法,接收参数为整数类型。
因为枚举值本身就是整数类型,因此我们选择ToObject方法作为最终实现,这样就避免使用Parse方法时还需要把整数类型参数进行转换。
同时我们通过上图可以发现枚举值可能的类型有uint、ulong、ushort、sbyte、long、int、byte、short八种情况。
因此下面我们以int类型作为示例,进行说明,但同时考虑到后面通用性、扩展性,我们再封装一个公共的泛型实现可以做到支持上面八种类型。因此本方法会调用一个内部私有方法,具体如下:
而内部私有方法即通过泛型实现对多种类型支持,我们先看代码实现再详细讲解,具体代码如下:
该方法首先验证参数合法性,验证通过直接使用ToObject方法进行转换。
参数验证首先通过Enum.IsDefined方法校验参数是否是有效的枚举项,这是因为无论是ToObject方法还是Parse方法对于整数类型参数都是可以转换成功的,无论这个参数是否是枚举中的项,因此我们需要首先排查掉非枚举中的项。
而该方法中IsValidFlagsMask方法主要是针对位标志枚举组合情况,位标志枚举特性导致即使我们枚举项中没有定义相关项,但是可以通过组合得到而且是合法的,因此我们需要对位标志枚举单独处理,具体实现代码如下:
该方法首先是判断当前枚举是否是位标志枚举即枚举是否带有Flags特性,可以通过Attribute.IsDefined实现,考虑到性能问题,因此我们把枚举是否为位标志枚举存入缓存中,当下次使用时就不必再次判断了。
如果当前枚举不是位标志枚举则之间返回false。
如果是位标志枚举则进入关键点了,如何判断一个值是否为一组值或一组值任意组合里面的一个?
这里用到了位掩码技术,通过按位或对所有枚举项进行标记,达到合并所有枚举项的目的,同时还包括可能的组合情况。
这里存储掩码的变量定义为long类型,因为我们需要兼容上文提到的八种整数类型。同时一个符合规范的位标志枚举设计枚举值是不会出现负数的因此也需要过滤掉。
同时考虑到性能问题,也需要把每个枚举对于的枚举掩码记录到缓存中方便下次使用。
拿到枚举掩码后我们需要对其进行取反,即表示所有符合要求的值,此值再与待验证参数做按位与操作,如果不为0表示待验证才是为无效枚举值,否则为有效枚举值。
关于位操作我们后面找机会再单独详解讲解其中原理和奥秘。
讲解完整个实现过程我们还需要对该方法进行详细的单元测试,具体分为以下几种情况:
(1) 正常枚举值,成功转换成枚举;
(2) 不存在的枚举值,但是可以通过枚举项按位或合并得到,返回空;
(3) 不存在的枚举值,也不可以通过枚举项按位或合并得到,返回空;
(4) 正常位标志枚举值,成功转换成枚举;
(5) 不存在的枚举值,但是可以通过枚举项按位或合并得到,成功转换成枚举;
(6) 不存在的枚举值,也不可以通过枚举项按位或合并得到,返回空;
具体实现代码如下:
02、根据枚举值转换成枚举或默认值
该方法是对上一个方法的补充,用于处理转换不成功时,则返回一个指定默认枚举,具体代码如下:
然后我们进行一个简单单元测试,代码如下:
03、根据枚举值转换成枚举名称
该方法接收枚举值作为参数,并转为对应枚举名称,转换失败则返回空。
实现则是通过根据枚举值转换成枚举方法获得枚举,然后通过枚举获取枚举名称,具体代码如下:
我们进行如下单元测试:
04、根据枚举值转换成枚举名称默认值
该方法是对上一个方法的补充,用于处理转换不成功时,则返回一个指定默认枚举名称,具体代码如下:
进行简单的单元测试,具体代码如下:
05、根据枚举值转换成枚举描述
该方法接收枚举值作为参数,并转为对应枚举名称,转换失败则返回空。
实现则是通过根据枚举值转换成枚举方法获得枚举,然后通过枚举获取枚举描述,具体代码如下:
单元测试如下:
06、根据枚举值转换成枚举描述默认值
该方法是对上一个方法的补充,用于处理转换不成功时,则返回一个指定默认枚举描述,具体代码如下:
单元测试代码如下:
稍晚些时候我会把库上传至Nuget,大家可以直接使用Ideal.Core.Common。
注:测试方法代码以及示例源码都已经上传至代码库,有兴趣的可以看看。https://gitee.com/hugogoos/Ideal
热门跟贴