大家还记得文件的 FileAttribute 属性吗?它可以同时拥有多个值,如同时具有 ReadOnly、Hidden、System 这些属性,但它的类型只是一个枚举类型,那么它是如何实现同时具有多值的呢?
大家知道,枚举值的每一项的实际值只是一个数字,默认情况下每一项都是顺序赋值的,即第一项是0,第二项是1,第 n 项是 n-1。如果将这个类型的变量赋的值大于 n 的值,则无效。虽然无效,但是程序并不会出错,这时我们就想想,如果将这个变量赋一个特殊的值,比如说:某一枚举类型有 10 个项,值从 0 - 9,这时如果将该类型的变量赋值为 17,虽然该值不在枚举项中,但你可以把它当做最后两项值的和,因为除此之外其它任何两个项的值之和都不可能为 17,所以我们就可以把 17 当做同时包含了两个值。
对于上面的例子,虽然我们可以那样认为是包含了两个值,但实际上有很多的三个项的值之和都为 17,这样就无法确定它到底包含了哪些项。那么,要想实现这样的需求,就必须要求每一个值都是唯一的一个项组合,不能出现有两组项之和相等。这样对于任何一个值,要么非常明确它就是枚举中的某一项的值,要么就是哪几项值之和,绝不可能有第三种情况。
那么如果对枚举类型中的每一项定义值就成了关键。顺序定义值肯定不行,会出现不唯一的情况。那么该如何定义?从排列组合里的乘法原理可以得知,将每一项的值定义为 2 的幂(即 1、2、4、8 等),这就能保证组合的值都不会重叠。
例:
Enum E颜色
透明 = 0
黑 = 1
红 = 2
绿 = 4
蓝 = 8
End Enum
当变量的值为 3 时,即包含黑、红;为 10 时,包含红、蓝;为 15 时,包含除透明外的所有项。
在 .Net 中,当使用 枚举变量.toString 时,会输出枚举项的名称,如上例中,值为 2 ,则输出“红”。但当变量的值不在枚举项中时,会直接输出数值,不是我们预期的输出所包含的项的名称,如当值为 3 时,会输出“3”而不是“黑,红”。当然, .Net 不会不想到这个问题,它提供了 FlagsAttribute 属性,在声明枚举类型时添加此属性即可。如上面的例子改为:
<FlagsAttribute( )> _
Enum E颜色
透明 = 0
黑 = 1
红 = 2
绿 = 4
蓝 = 8
End Enum
这样在输出时会显示出所包含项的名称。当然,这个无关紧要,无非是显示的事,和我们需要的没关系。
好了,通过上面的描述,我们已经知道如何在一个值里存放多个值(看着这么矛盾-_-#),那么如何操作呢。还用上面的例子,具体操作如下:
Dim c As E颜色
添加:
c = c Or E颜色.黑
移除:
c = c Xor E颜色.黑
判断是否存在:
c And E颜色.黑 = E颜色.黑
OK!看到这儿大家应该明白了,判断是否存在就可以用来保存一个 Boolean 值了。比如需要将 10 个 Boolean 值存入数据库中的某字段,先将字段类型设为整型,再定义一个枚举类型,将这 10 项按 2 的幂的值定义到这个枚举类型中。若某个值为 True,则添加,否则就移除。检索值时用判断是否存在即可。
其实也不一定非要用枚举,只要按这个规则,自定义一个类更方便,如:
Private _value As UInt64
Public Sub Add( ByVal value As UInt64)
_value = _value Or value
End Sub
Public Sub Remove( ByVal value As UInt64)
_value = _value Xor value
End Sub
Public Function Exist( ByVal value As UInt64) As Boolean
Return _value = _value And value
End Function
Public Sub New ( ByVal value As UInt64)
_value = value
End Sub
End Class
有什么意义?
如果在开发过程中发现还需要在数据库中添加一个字段,这一定是非常令人痛苦的,要添加大量的代码。不过如果添加的是一个 Boolean 字段,就可以不用改动数据库了。比如,先前数据库的信息删除时都是直接删除,现在需要多一个回收站的功能,需要在数据库中添加一个字段,标识记录是否被删除;或者需要添加一个字段标识记录的审核状态等等,只需要定义一个私有变量,其值为2的N次方,或是在枚举中添加一项,然后再添加其属性即可,修改的只是业务层的代码,甚至可以在运行时动态添加,很是方便。而且在进行条件查询时是用位运算进行条件判断的,效率也非常高。缺点就是占用了更多的字节。