• 适当使用enum做数据字典


        在一些应用中,通常会用到很多由一些常量来进行描述的状态数据,比如性别(男、女),审核(未审核、已审核)等。在数据库中一般用数字形式来存储,比如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>
    这个目前还只是想法,也许有空会去实现它。
    或许这么做看起来会有些繁锁,但为了灵活性考虑,这样子去做还是值得的,另外也是看个人喜好了吧。
  • 相关阅读:
    jquery 的 outerWidth() 、width() 、innerWidth()
    图片自动切换 避免 鼠标快速滑过
    Ajax中日历控件的使用
    asp.net如何读取xml文件中的数据
    ASP.NET使用AspNetPager实现简单的分页功能
    XmlDataDocument与DataSet相互转化
    C#中如何过滤掉多余的html代码
    asp.net的几种经典的页面传值方法
    ASP.Net分页方法详解
    ASP.Net中省市级联有关
  • 原文地址:https://www.cnblogs.com/lwme/p/1887026.html
Copyright © 2020-2023  润新知