在C#编程中玩转枚举,分享我的EnumHelper。

在软件开发过程中,我们经常会为特定的场景下的特定数据定义逻辑意义。比如在用户表中,我们可能会有一个用户状态字段,该字段为整形。如果该字段的值为1则代表用户状态正常,2则代表用户被锁定等等。这些规则应该被写入开发文档里,但是每次都去查文档,也是一件痛苦的事情。其实,在C#中有一个很简单的方法可以实现数据和表象意义之间的转换。枚举既是为此而生。

例如,我们有一个用户状态的枚举,它看起来像这个样子:

 
 
 
 
 

C#

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    /// <summary>
    /// 用户状态
    /// </summary>
    public enum UserState
    {
        /// <summary>
        /// 未激活
        /// </summary>
        [Description("未激活")]
        Nonactivated = 0,
        /// <summary>
        /// 正常
        /// </summary>
        [Description("正常")]
        Normal = 1,
        /// <summary>
        /// 锁定
        /// </summary>
        [Description("锁定")]
        Locked = 2
    }

枚举名称用于区分类型,枚举值用于程序判断,Description特性专为显示而生。多么美好的配合。

获取枚举值的Description信息

 
 
 
 
 

C#

 
1
2
3
4
5
6
7
8
9
        public static String GetDescription(Enum value)
        {
            Type type = value.GetType();
            FieldInfo item = type.GetField(value.ToString(), BindingFlags.Public | BindingFlags.Static);
            if (item == null) return null;
            var attribute = Attribute.GetCustomAttribute(item, typeof(DescriptionAttribute)) as DescriptionAttribute;
            if (attribute != null && !String.IsNullOrEmpty(attribute.Description)) return attribute.Description;
            return null;
        }

枚举除了用于定义和区分状态之外,也可以参与运算。基于枚举的位操作常常用于权限管理中。多个权限操作可以存储在同一个字段中,而不用在数据表中增加N多列,想想就觉得美好。一个常见的操作权限枚举定义如下:

代码来自 XCode 组件

 
 
 
 
 

C#

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
    /// <summary>操作权限</summary>
    [Flags]
    [Description("操作权限")]
    public enum PermissionFlags
    {
        /// <summary>无权限</summary>
        [Description("无")]
        None = 0,
 
        /// <summary>所有权限</summary>
        [Description("所有")]
        All = 1,
 
        /// <summary>添加权限</summary>
        [Description("添加")]
        Insert = 2,
 
        /// <summary>修改权限</summary>
        [Description("修改")]
        Update = 4,
 
        /// <summary>删除权限</summary>
        [Description("删除")]
        Delete = 8,
 
        /// <summary>自定义1权限</summary>
        /// <remarks>这里没有接着排16,为了保留给上面使用</remarks>
        [Description("自定义1")]
        Custom1 = 0x20,
 
        /// <summary>自定义2权限</summary>
        [Description("自定义2")]
        Custom2 = Custom1 * 2,
 
        /// <summary>自定义3权限</summary>
        [Description("自定义3")]
        Custom3 = Custom2 * 2,
 
        /// <summary>自定义4权限</summary>
        [Description("自定义4")]
        Custom4 = Custom3 * 2,
 
        /// <summary>自定义5权限</summary>
        [Description("自定义5")]
        Custom5 = Custom4 * 2,
 
        /// <summary>自定义6权限</summary>
        [Description("自定义6")]
        Custom6 = Custom5 * 2,
 
        /// <summary>自定义7权限</summary>
        [Description("自定义7")]
        Custom7 = Custom6 * 2,
 
        /// <summary>自定义8权限</summary>
        [Description("自定义8")]
        Custom8 = Custom7 * 2
    }

如果我们要为用户设定添加和删除权限,只需要为用户的操作权限值设定为10即可(添加权限值为2,删除权限值为8,加起来值为10)。要验证用户是否包含某权限,只需要将该权限与用户拥有的权限值做位运算即可。

判断权限码是否包含添加权限

 
 
 
 
 

C#

 
1
2
3
4
5
6
7
8
9
10
            var code = 10;
            var flag = (PermissionFlags)code;
            if ((PermissionFlags.Insert & flag) == flag)
            {
                Console.WriteLine("存在添加权限");
            }
            else
            {
                Console.WriteLine("没有添加权限");
            }

在程序圈里摸爬滚打这几年,也勉为其难的步入“三流程序员”的行列。封装一下吧,要对得起自己学过的面向对象。(三流指:封装、继承、多态。)

 
 
 
 
 

C#

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
    [EditorBrowsable(EditorBrowsableState.Never)]
    public static class EnumHelper
    {
        public static T Set<T>(this Enum source, T flag, Boolean value)
        {
            if (!(source is T)) throw new ArgumentException("枚举标识判断必须是相同的类型!", "source");
            ulong s = Convert.ToUInt64(source);
            ulong f = Convert.ToUInt64(flag);
 
            if (value)
            {
                // 必须先检查是否包含这个标识位,因为异或的操作仅仅是取反
                if ((s & f) != f) s ^= f;
            }
            else
                s = s | f;
 
            return (T)Enum.ToObject(typeof(T), s);
        }
 
        public static Boolean Has(this Enum value, Enum flag)
        {
            if (value.GetType() != flag.GetType()) throw new ArgumentException("枚举标识判断必须是相同的类型!", "flag");
            ulong num = Convert.ToUInt64(flag);
            return (Convert.ToUInt64(value) & num) == num;
        }
 
        public static String GetDescription(this Enum value)
        {
            Type type = value.GetType();
            FieldInfo item = type.GetField(value.ToString(), BindingFlags.Public | BindingFlags.Static);
            if (item == null) return null;
            var attribute = Attribute.GetCustomAttribute(item, typeof(DescriptionAttribute)) as DescriptionAttribute;
            if (attribute != null && !String.IsNullOrEmpty(attribute.Description)) return attribute.Description;
            return null;
        }
 
        public static Dictionary<T, String> GetDescriptions<T>() where T : struct
        {
            return GetDescriptions<T>(typeof(T));
        }
 
        public static Dictionary<T, String> GetDescriptions<T>(Type type)
        {
            var dic = new Dictionary<T, String>();
            foreach (FieldInfo item in type.GetFields(BindingFlags.Public | BindingFlags.Static))
            {
                if (!item.IsStatic) continue;
                var value = (T)item.GetValue(null);
                string des = item.Name;
                var dna = Attribute.GetCustomAttribute(item, typeof(DisplayNameAttribute)) as DisplayNameAttribute;
                if (dna != null && !String.IsNullOrEmpty(dna.DisplayName)) des = dna.DisplayName;
 
                var att = Attribute.GetCustomAttribute(item, typeof(DescriptionAttribute)) as DescriptionAttribute;
                if (att != null && !String.IsNullOrEmpty(att.Description)) des = att.Description;
                dic.Add(value, des);
            }
            return dic;
        }
    }

但是,做到这里还不够,我们还需要更多的东西来支持界面显示。用于应付在Web开发中常用数据展示和筛选需求。当然,WinForm也可以,只不过需要看官自己去实现。

为WebForm扩展,用于在Repeater控件中展示:

为WebForm扩展,用于在Repeater控件中展示。

 
 
 
 
 

C#

 
1
2
3
4
5
        public static String GetDescription<T>(this Page page, Object value)
        {
            var t = (T)(Convert.ToInt32(value) as Object);
            return (t as Enum).GetDescription();
        }

调用示例:(前提是你得在Web.config中引入相应的命名空间)

 
 
 
 
 

XHTML

 
1
<td><%#this.GetDescription<UserState>( Eval("STATE")) %></td>

为DropDownList扩展绑定:

为DropDownList扩展枚举绑定

 
 
 
 
 

C#

 
1
2
3
4
5
6
7
8
9
10
        public static void BindItem<T>(this DropDownList ddl, Boolean allowEmpty = false) where T : struct
        {
            var dic = Toolkit.Extension.EnumHelper.GetDescriptions<T>();
            ddl.Items.Clear();
            if (allowEmpty) ddl.Items.Add(new ListItem("==请选择==", String.Empty));
            foreach (var item in dic)
            {
                ddl.Items.Add(new ListItem(item.Value, Convert.ToInt32(item.Key).ToString("D")));
            }
        }

调用示例:

 
 
 
 
 

C#

 
1
this.ddlState.BindItem<Common.Define.UserState>();

这些就是我工作以来在项目中使用枚举所带来的经验。这种做法大大的提高了编程的效率,可以让程序员更关注业务实现,而不必再为数据为0到底是什么意思扯皮。文中代码来自于真实项目,在你没有用错的情况下可以保证可用性。代码在很大程度上参考了X组件,再次对@大石头表示感谢。如果你感兴趣,可以来新生命团队做客。

呵呵,声明~

◆◆0
 

最后编辑于:2014/6/12作者: Soar、毅

.NET 程序员,默默无闻的码农,一直希望行走的很文艺的苦逼青年.

在C#编程中玩转枚举,分享我的EnumHelper。的更多相关文章

  1. [翻译] C# 8.0 新特性 Redis基本使用及百亿数据量中的使用技巧分享(附视频地址及观看指南) 【由浅至深】redis 实现发布订阅的几种方式 .NET Core开发者的福音之玩转Redis的又一傻瓜式神器推荐

    [翻译] C# 8.0 新特性 2018-11-13 17:04 by Rwing, 1179 阅读, 24 评论, 收藏, 编辑 原文: Building C# 8.0[译注:原文主标题如此,但内容 ...

  2. Python中模拟enum枚举类型的5种方法分享

    这篇文章主要介绍了Python中模拟enum枚举类型的5种方法分享,本文直接给出实现代码,需要的朋友可以参考下   以下几种方法来模拟enum:(感觉方法一简单实用) 复制代码代码如下: # way1 ...

  3. 一文读懂高性能网络编程中的I/O模型

    1.前言 随着互联网的发展,面对海量用户高并发业务,传统的阻塞式的服务端架构模式已经无能为力.本文(和下篇<高性能网络编程(六):一文读懂高性能网络编程中的线程模型>)旨在为大家提供有用的 ...

  4. 第51讲:Scala中链式调用风格的实现代码实战及其在Spark编程中的广泛运用

    今天学习了下scala中的链式调用风格的实现,在spark编程中,我们经常会看到如下一段代码: sc.textFile("hdfs://......").flatMap(_.spl ...

  5. perl编程中的map函数示例

    转自:http://www.jbxue.com/article/14854.html 发布:脚本学堂/Perl  编辑:JB01   2013-12-20 10:20:01  [大 中 小] 本文介绍 ...

  6. Android编程中的实用快捷键

    作为一个优秀的程序员,不但要能开发出漂亮的软件,也要能熟练掌握编程的技巧,包括IDE的快捷键使用.比如linux 下的VI编辑器,对于不熟练快捷键的人来说就是一个噩梦,但一旦你熟练了VI的快捷键,VI ...

  7. Java 数据类型在实际开发中应用二枚举

    在实际编程中,往往存在着这样的"数据集",它们的数值在程序中是稳定的,而且"数据集"中的元素是有限的.在JDK1.5之前,人们用接口来描述这一种数据类型. 1. ...

  8. lazy ideas in programming(编程中的惰性思想)

    lazy形容词,懒惰的,毫无疑问是一个贬义词.但是,对于计算机领域,lazy却是非常重要的优化思想:把任务推迟到必须的时刻,好处是避免重复计算,甚至不计算.本文的目的是抛砖引玉,总结一些编程中的laz ...

  9. (转)Attribute在.net编程中的应用

    Attribute在.net编程中的应用(一)Attribute的基本概念 经常有朋友问,Attribute是什么?它有什么用?好像没有这个东东程序也能运行.实际上在.Net中,Attribute是一 ...

随机推荐

  1. DuiVision开发教程(15)-DUI文本控制基础类

    CControlBaseFont类是DuiVision支持所有基类的控件的文本属性. 此控件例如属性列表,下面: 物业名称 类型 说明 title 字符串 控件的显示标题 font 字体 控件的字体, ...

  2. css javascript 自动化压缩(保存后即自动生成压缩文件)

    先上图:

  3. SQL 2008执行语句遇到内存不足(1)——error 701

    原文:SQL 2008执行语句遇到内存不足(1)--error 701 转自:http://blogs.msdn.com/b/apgcdsd/archive/2011/01/17/sql-2008-e ...

  4. 用python做oj上的简单题(持续更新中.......)

    本人刚開始接触python,在oj上解一些简单的题,欢迎交流,不喜勿喷. OJ地址链接:acm.sdut.edu.cn http://acm.sdut.edu.cn/sdutoj/showproble ...

  5. ashx一般处理程序和HttpHandler

    asp.net项目中,使用.ashx的文件(一般处理程序)可以用于处理客户端发送来的请求,并将服务器端的处理结果返回给客户端.它能返回的类型可以是文本.或者图片.有时候,我们可以在项目中使用.cs的文 ...

  6. boost准模板库scoped_ptr指针的使用以及auto_ptr智能指针的对照

    首先我们看看scoped_ptr的基本使用,包括了swap(),get(),reset()的使用,重要的提醒是作用域结束的时候会自己主动析构,无需手动的释放资源: #include<boost/ ...

  7. 连载:面向对象的葵花宝典:思考、技巧与实践(39) - 设计原则 vs 设计模式

    它的设计原则,和设计模式,是否该用它? ============================================================================= 在& ...

  8. Linux内核分析(二)----内核模块简介|简单内核模块实现

    原文:Linux内核分析(二)----内核模块简介|简单内核模块实现 Linux内核分析(二) 昨天我们开始了内核的分析,网上有很多人是用用源码直接分析,这样造成的问题是,大家觉得很枯燥很难理解,从某 ...

  9. ScrollView 在嵌套 ViewPager 时出现的问题

    1.在ViewPager 外面嵌套ScrollView 时导致ViewPager 中内容不显示,解决的办法是在ScrollView 标签下增加 android:fillViewport="t ...

  10. 以太网PHY 芯片之 MII/MDIO接口详解

    本文主要分析MII/RMII/SMII,以及GMII/RGMII/SGMII接口的信号定义,及相关知识,同时本文也对RJ-45接口进行了总结,分析了在10/100模式下和1000M模式下的设计方法. ...