• c#中的特性Attribute


    一:特性是什么?特性怎么创建怎么使用?

    这一章节,我想谈谈c#的特性方面的知识,特性大家在工作开发中都很熟悉,比如我们经常见到的

    1:key Display --EF
    2:Import
    3:HttpGet HttpPost HttpDelete --MVC/WebApi
    4:Serializable,Obsolete --系统单独功能
    5:AutoMapFrom
    6:WebMethod --WebService
    7:Injection InjectionConstractor InjectMethod --IOC的特性 
    这些特性我们运用时感觉很简单,只需要简简单单的在类或者属性或者方法上面加 [特性的名字] 有时候就能实现我们想要的效果,那今天我们就来揭开特性的面纱,看看特性到底是什么以及是怎么实现一些功能的?如想知道,请继续往下看!
     
     
    通过使用反编译工具看IL中间语言发现,特性就是一个类,直接继承/间接继承Attribute父类,约定俗称使用Attribute结尾,标记的时候可以省略掉,可以使用[]包裹标记到元素,其实就调用的无参构造函数, 特性起到标记的作用,从而影响编译器[Obsolete] 或者影响程序的运行([Serializable])等,下面我们创建一个特性如下:
        /// <summary>
        /// 是给枚举用  提供一个额外信息
        /// AllowMultiple特性影响编译器,AttributeTargets修饰的对象 AllowMultiple:能否重复修饰 Inherited:是否可继承
        /// 可以指定属性和字段
        /// </summary>
        [AttributeUsage(AttributeTargets.Enum | AttributeTargets.Field,AllowMultiple =true,Inherited =true)]
        public class RemarkAttribute : Attribute
        {
            public RemarkAttribute()
            {
            }
            public RemarkAttribute(string remark)
            {
                this.Remark = remark;
            }
            public string Description; //字段
            public string Remark { get; private set; }
        }

    然后特性类上面的AttributeUsage也是一个特性,具体的用处见上面的注释解释,特性创建好了,我们可以创建一个实体,然后再实体的字段上面使用这个特性,具体如下:

        [Remark("用户状态")]
        public enum UserState
        {
            /// <summary>
            /// 正常
            /// </summary>
            [Remark("正常")]
            Normal = 0,
            /// <summary>
            /// 冻结
            /// </summary>
            [Remark("冻结")]
            Frozen = 1,
            /// <summary>
            /// 删除
            /// </summary>
            [Remark("删除")]
            Deleted = 2
        }

    因为AttributeTargets.Enum标识这个特性可以放在枚举上面,那我们上面那种代码也是完全被允许的,此外,AttributeTargets如果不写,则默认是全部都可以使用的!特性创建和特性标识已经准备好了,然而怎么使特性生效呢?这个还是需要我们写代码来实现的

     public static class RemarkExtend
     {
            /// <summary>
            /// 扩展方法
            /// </summary>
            /// <param name="enumValue"></param>
            /// <returns></returns>
            public static string GetRemark(this Enum enumValue)
            {
                Type type = enumValue.GetType();
                FieldInfo field = type.GetField(enumValue.ToString());
                if (field.IsDefined(typeof(RemarkAttribute), true))
                {
                    RemarkAttribute remarkAttribute = (RemarkAttribute)field.GetCustomAttribute(typeof(RemarkAttribute));
                    return remarkAttribute.Remark;
                }
                else
                {
                    return enumValue.ToString();
                }
            }
        }

    使特性的生效的代码已经写好了,然后我们如果想要获得一个枚举的中文名字可以通过下面调用

     UserState userState = UserState.Normal;
     Console.WriteLine(userState.GetRemark());

    这样我们就可以随心所欲的获取到这个枚举的中文备注了,没有使用特性之前,我们想要获取到枚举的中文备注,一般会只能写死判断,如下:

     UserState userState = UserState.Normal;
     if (userState == UserState.Normal)
     {
            Console.WriteLine("正常状态");
     }
     else if (userState == UserState.Frozen)
     { 
            Console.WriteLine("冻结状态"); 
    }
    else
    { 
            Console.WriteLine("删除状态"); 
    }

    然后一旦要修改状态名字,则代码也随着修改,这就会导致维护比较困难,如果使用了特性,则会用到的地方不需要太关注这些文字,只需要调用,然后修改的时候,只需要修改枚举上面的文字即可,极其容易维护!

    二:特性的运用范围

    特性运用范围极其广,框架中我们看到上面的总结就晓得了,下面的我总结了以下几个地方也可以使用特性

    1:数据展示 不想展示属性名字,而是用中文描述
    2:想指定哪个是主键,哪个是自增
    3:别名 数据库里面叫A 程序是B,怎么映射等

    然后特性还可以校验一些数据字段等,下面我写个例子,以便更加容易理解:

    1:先创建数据校验的特性

     public abstract class AbstractValidateAttribute : Attribute
        {
            public abstract bool Validate(object oValue);
        }
    
        public class LongValidateAttribute : AbstractValidateAttribute
        {
            private long _lMin = 0;
            private long _lMax = 0;
            public LongValidateAttribute(long lMin, long lMax)
            {
                this._lMin = lMin;
                this._lMax = lMax;
            }
    
            public override bool Validate(object oValue)
            {
                return this._lMin < (long)oValue && (long)oValue < this._lMax;
            }
        }
        public class RequirdValidateAttribute : AbstractValidateAttribute
        {
            public override bool Validate(object oValue)
            {
                return oValue != null;
            }
        }
    View Code

    2:再一个实体类上面的字段增加特性

    public class Student
     {
            
            [RequirdValidate]
            public int Id { get; set; }
    
            [LongValidate(5,10)]//还有各种检查
            public string Name { get; set; }
            [LongValidate(20, 50)]
            public string Accont { get; set; }
    
            /// <summary>
            /// 10001~999999999999
            /// </summary>
            [LongValidate(10001, 999999999999)]
            public long QQ { get; set; }
            
        }
    View Code

    3:写下面的方法,使特性生效

    public class DataValidate
    {
            public static bool Validate<T>(T t)
            {
                Type type = t.GetType();
                //IsDefined 是判断,不会调用构造函数
                //if (type.IsDefined(typeof(AbstractValidateAttribute), true))
                //{
                //    //调用构造函数
                //    var oAttributeArray = type.GetCustomAttributes(typeof(AbstractValidateAttribute), true);
                //    foreach (var item in oAttributeArray)
                //    {
    
                //    }
                //}
                //foreach (var method in type.GetMethods())
                //{
                //    if (method.IsDefined(typeof(AbstractValidateAttribute), true))
                //    {
                //        object item = method.GetCustomAttributes(typeof(AbstractValidateAttribute), true)[0];
                //        AbstractValidateAttribute attribute = item as AbstractValidateAttribute;
                //        //if (!attribute.Validate(method.GetValue(t)))
                //        //{
                //        //    result = false;
                //        //    break;
                //        //}
                //    }
                //}
                bool result = true;
                foreach (var prop in type.GetProperties())
                {
                    if (prop.IsDefined(typeof(AbstractValidateAttribute), true))
                    {
                        object item = prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true)[0];
                        AbstractValidateAttribute attribute = item as AbstractValidateAttribute;
                        if (!attribute.Validate(prop.GetValue(t)))
                        {
                            result = false;
                            break;
                        }
                    }
                }
                return result;
            }
        }
    View Code

    4:应用判断时如下:

    Student student = new Student();
    student.Id = 123;
    student.Name = "MrSorry";
    student.QQ = 123456;
    var result = DataValidate.Validate(student);
    View Code

    这样就完成了数据校验,特性校验的特点,总结得到了如下三点:

    1:可以校验多个属性
    2:可以支持多重校验
    3:支持规则的随意扩展

    运用了这个特性校验后,就不用再每个地方再分别写校验的逻辑,简单易用!

  • 相关阅读:
    Linux学习(五)
    Linux学习(四)
    Linux学习(三)
    Linux学习(二)
    Linux学习(一)
    JAVA学习笔记(九)
    JAVA学习笔记(八)
    连接报错'mysql_native_password'
    TabControl改变TabPage时自动字体变大
    问题:winform窗体与设计时不一致
  • 原文地址:https://www.cnblogs.com/loverwangshan/p/10153832.html
Copyright © 2020-2023  润新知