在一些应用中,通常会用到很多由一些常量来进行描述的状态数据,比如性别(男、女),审核(未审核、已审核)等。在数据库中一般用数字形式来存储,比如0、1等。

不好的做法

经常看到一些应用(ps:最近又看到)没有把这类数据整理成一个数据字典,比如经常重复这样的html:

<select>
    <option value="0">未审核</option>
    <option value="1">已审核</option>
</select>

然后在后端逻辑判断的时候又用这样的代码:

if (xx == "0") {
    //...
} else if (xx == "1") {
    //...
}

在显示数据的时候又出现这样的代码:

switch(xx) {
    case "0":
        return "未审核";
    case "1":
        return "已审核";
}

这样的代码不仅不利于维护,而且,可读性也是非常的差。

使用enum改造

对于以上这类比较固定而且数据量又比较小的状态,可以选择使用enum来改造它,首先定义这么一个枚举类型:

enum AuditState {
    //未审核
    UnAudit = 0,
    //已审核
    Audited
}

代表了审核的2种状态,这样在进行判断的时候就可以比较直观的看出来了。但是对于数据绑定,这样做还不够,所以我选择使用Attribute来描述:

[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
public class EnumDescriptionAttribute : Attribute
{
    public EnumDescriptionAttribute(string description)
    {
        this.Description = description;
    }
     
    public string Description { get; set; }
}

现在,关于审核状态的枚举类型就变成这样了:

enum AuditState {
    //未审核
    [EnumDescription("未审核")]
    UnAudit = 0,
    //已审核
    [EnumDescription("已审核")]
    Audited
}

然后可以通过取出它的值以及EnumDescription,来实现绑定,无论在可读性还是维护上都是方便许多。

使用缓存

由于获取EnumDescriptionAttribute值是需要通过反射来实现的,如果每次都反射调用,那么性能将会比较糟糕。另外,调用Enum.GetNames、Enum.GetValues以及具体的如AuditState.UnAudit.ToString()方法也都是需要反射的,所以也要注意。

在这里使用静态变量来缓存这些信息,并且利用了泛型的特性,保证每种枚举类型都有一个对应的缓存信息,代码如下:

//T为枚举类型,V为枚举值的类型
class EnumHelper<T, V> where T : struct where V : struct
{
    private static IDictionary<T, string> enumAndDescriptionCache;//描述信息缓存   
    private static IDictionary<V, string> valueAndDescriptionCache;
 
    static EnumHelper()
    {
        Initialize();
    }
 
    /// <summary>
    /// 初始化
    /// </summary>
    private static void Initialize()
    {
        Type type = typeof(T);
        if (!type.IsEnum)
            throw new ArgumentException("Generic type must be an enumeration");
 
        string[] enumNames = Enum.GetNames(type);
        V[] enumValues = Enum.GetValues(type) as V[];
        T[] enums = Enum.GetValues(type) as T[];
        int l = enumNames.Length;
 
        enumAndDescriptionCache = new Dictionary<T, string>(l);
        valueAndDescriptionCache = new Dictionary<V, string>(l);
 
        EnumDescriptionAttribute tempAttr;
        string temp;
        for (int i = 0; i < l; i++)
        {
            tempAttr = GetDescriptionAttr(type.GetField(enumNames[i]));
            temp = tempAttr == null ? string.Empty : tempAttr.Description;
            enumAndDescriptionCache.Add(enums[i], temp);
            valueAndDescriptionCache.Add(enumValues[i], temp);
        }
    }
 
    /// <summary>
    /// 获取枚举类型的描述信息,并加入到缓存中
    /// </summary>
    /// <param name="f"></param>
    /// <param name="value"></param>
    private static EnumDescriptionAttribute GetDescriptionAttr(FieldInfo f)
    {
        EnumDescriptionAttribute[] attrs = f.GetCustomAttributes(typeof(EnumDescriptionAttribute), false) as EnumDescriptionAttribute[];
        if (attrs != null && attrs.Length > 0)
        {
            return attrs[0];
        }
        return null;
    }
 
    /// <summary>
    /// 获取枚举类型的描述
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static string GetDescription(T value)
    {
        string description = null;
 
        if (enumAndDescriptionCache.ContainsKey(value))
            enumAndDescriptionCache.TryGetValue(value, out description);
 
        return description;
    }
     
    /// <summary>
    /// 获取枚举类型的描述
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    public static string GetDescriptionByValue(V value)
    {
        string description = null;
 
        if (valueAndDescriptionCache.ContainsKey(value))
            valueAndDescriptionCache.TryGetValue(value, out description);
 
        return description;
    }
 
    /// <summary>
    /// 获取枚举类型所有值及描述
    /// </summary>
    public static IEnumerable<KeyValuePair<T, string>> EnumDescriptions
    {
        get
        {
            foreach (KeyValuePair<T, string> temp in enumAndDescriptionCache)
            {
                yield return temp;
            }
        }
    }
     
    /// <summary>
    /// 获取枚举类型所有值及描述
    /// </summary>
    public static IEnumerable<KeyValuePair<V, string>> ValueDescriptions
    {
        get
        {
            foreach (KeyValuePair<V, string> temp in valueAndDescriptionCache)
            {
                yield return temp;
            }
        }
    }
}

具体使用

对于数据绑定可以这样子做:

<select>
    <asp:Repeater runat="server" id="xx">
    <ItemTemplate>
        <option value="<%# Eval("Key")%>"><%# Eval("Value")%></option>
    </ItemTemplate>
    </asp:Repeater>
</select>

后端代码:

x.DataSource = EnumHelper<AuditState, int>.ValueDescriptions;
x.DataBind();

对以上这种数据绑定可以专门封装成一个用户控件,以便重复利用。

显示数据的时候可以这样:

<asp:Repeater runat="server" id="xx">
<ItemTemplate>
    <%#EnumHelper<AuditState, int>.GetDescription(Convert.ToInt32(Eval("fromdb..")))%>
</ItemTemplate>
</asp:Repeater>

判断的时候也可以很直观:

if (xxxx == AuditState.Audited) //...

当然,使用的这些存在类型转换的地方还有待优化,比如到int32类型的转换、到AuditState类型的转换,都可以通过对EnumHelper类进行修改来完成,这里只是为了演示,就不再具体了。

更多需求

以上的方法只能适用于一种语言中,假如还需要显示英文的描述,那么就需要对以上的类型进行调整了。比如可以为EnumDescriptionAttribute添加一个属性:

public string Description { get; set; }
public string EnDescription { get; set; }

或者是再创建一个名为EnEnumDescriptionAttribute的类型,无论是哪种方法,都需要在EnumHelper类里面做更多的调整了。

个人认为更好的做法是使用外部xml文件,EnumHelper根据需要加载相应语言的描述文件,如:

public class EnumDescriptionFileAttribute : Attribute
{
    public EnumDescriptionFileAttribute(string lang, string filepath)
    {
        this.Lang = lang;
        this.Filepath = filepath;
    }   
    public string Lang { get; set; }
    public string Filepath { get; set; }
}
 
[EnumDescriptionFile("AuditState.xml")]
enum AuditState {
    UnAudit = 0,
    Audited
}
 
class EnumHelper...{
    void LoadDescriptionFile(lang...) {
        //...
    }
    string GetDescription(lang...) {
        //...
    }
}
 
<lang value="zh-cn">
    <field name="UnAudit" value="未审核" />
    <field name="Audited" value="已审核" />
</lang>
<lang value="en">
    <field name="UnAudit" value="UnAudit" />
    <field name="Audited" value="Audited" />
</lang>
这个目前还只是想法,也许有空会去实现它。
或许这么做看起来会有些繁锁,但为了灵活性考虑,这样子去做还是值得的,另外也是看个人喜好了吧。

适当使用enum做数据字典 ( .net c# winform csharp asp.net webform )的更多相关文章

  1. 拦截asp.net输出流做处理, 拦截HTML文本(asp.net webForm版)

    对已经生成了HTML的页面做一些输出到客户端之前的处理 方法的原理是:把Response的输出重定向到自定义的容器内,也就是我们的StringBuilder对象里,在HTML所有的向页面输出都变 成了 ...

  2. Ninject 在 Winform、 Asp.net MVC中连络EntityFramework的应用

    Ninject 在 Winform. Asp.net MVC中连络EntityFramework的应用( 注入同一个DbContext的应用名人名言:科学是老老实实的东西,它要靠许许多多人民的劳动和智 ...

  3. WinForm中 Asp.Net Signalr消息推送测试实例

    p{ text-align:center; } blockquote > p > span{ text-align:center; font-size: 18px; color: #ff0 ...

  4. 做个无边框winform窗体,并美化界面

    今天下午程序写完,有些时间就搞下界面美化,做个无框窗体.首先把窗体的FormBorderStyle设置为None,就变成无框的啦,不过你会发现这样窗体上就没有原来的最大最小化和关闭按钮了哦,所以要自己 ...

  5. C#中正确使用enum做Key的姿势

    C#中自定义enum,然后将其作为Dictionary的Key,通常的做法如下: using System; using System.Text; using System.Collections.G ...

  6. Dictionary里使用struct,enum做key

    首先看下Dictionary的源码 public void Add (TKey key, TValue value) { if (key == null) throw new ArgumentNull ...

  7. 做个简单的RSS订阅(ASP.NET Core),节省自己的时间

    0x01 前言 因为每天上下班路上,午休前,都是看看新闻,但是种类繁多,又要自己找感兴趣的,所以肯定会耗费不少时间. 虽说现在有很多软件也可以订阅一些自己喜欢的新闻,要安装到手机,还是挺麻烦的.所以就 ...

  8. log4net在WinForm和ASP.net下的设置

    下载log4net.dll,放到bin目录下,然后引用到工程.下面说明配置和调用方法. 1.AssemblyInfo.cs末尾添加 [assembly: log4net.Config.XmlConfi ...

  9. C#、WinForm、ASP.NET - Md5散列加密

     MD5值概念解释: 转载自:http://free0007.iteye.com/blog/2047163 所 谓MD5,即"Message-Digest Algorithm 5(信息-摘要 ...

随机推荐

  1. 使用CKplayer插件在网页中嵌入视频的方法(常用笔记2)

    在做网站中有时候我们需要在网页中嵌入视频,一般视频嵌入有以下几种方法: 1. 优酷代码嵌入 优点:简单,方便,可靠. 缺点:有广告,现在的网站非常注重用户体验,如果打开一个在线视频是有长广告的一定会崩 ...

  2. StackExchange.Redis通用封装类分享

    前两天朋友问我,有没有使用过StackExchange.Redis,问我要个封装类,由于之前都是使用ServiceStack.Redis,由于ServiceStack.Redis v4版本后是收费版的 ...

  3. Mysql高并发优化

    一.数据库结构的设计 1.数据行的长度不要超过8020字节,如果超过这个长度的话在物理页中这条数据会占用两行从而造成存储碎片,降低查询效率. 2.能够用数字类型的字段尽量选择数字类型而不用字符串类型的 ...

  4. java servlet调用带有多个返回结果集的存储过程

    一.mysql存储过程 这里我先说下我这个功能实现的逻辑及途中遇到的一些问题.这个存储过程一共带两个输入参数,一共关联到两张表的查询,每个参数都对应查询表中的一个判断,所以一共返回了两个结果集(当然要 ...

  5. Oracle 数据库基础学习 (二) 学习小例子:创建一个表,记录商品买卖的情况

      运行环境:Oracle database 11g + PL/SQL Developer ex: --创建一个表 create table plspl_test_product( --加入not n ...

  6. PDF.NET 开发框架之 SOD框架 Ver 5.2 正式版开源源码发布

    PDF.NET 开发框架之 SOD框架 Ver 5.2.1.0307 正式版发布,包含以下部分: SOD_Pwmis.Core --包括下列数据提供程序 SqlServer SqlServerCe A ...

  7. 15款最佳的MySQL管理工具和应用程序

    工欲善其事,必先利其器.几乎每个开发人员都有最钟爱的 MySQL 管理工具,它帮助开发人员在许多方面支持包括 PostgreSQL,MySQL,SQLite,Redis,MongoDB 等在内的多种数 ...

  8. HTML中表单提交数据GET、POST的区别

    表单提交数据Get和Post的区别: GET和POST是表单提交数据其中的两种方式,除此之外还有PUT.DELETE等. GET: GET的请求起因于正常的URL请求,或是没有指定METHOD的HTM ...

  9. 【javascript激增的思考02】模块化与MVC

    前言 之前我们遇到了这么一个项目,也就是我们昨天提到的,有很多的小窗口的,昨天说的太抽象了,今天我们再来理一理什么是小窗口(后面点说下),当时由于js有一点复杂,我自己也装B跟风用了一下传说中MVC! ...

  10. easyui日期在未加载easyui-lang-zh_CN.js出现英文的情况下加载中文的方法

    我们有时候在操作easyui的时候本来是加载了easyui-lang-zh_CN.js中文文件包,但是还是出现了英文.使得我们不得埋怨这框架咋这么不好用,其实我们仔细看看这个中文包就会发现里面很多都是 ...