胖胖滴加菲猫

导航

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:支持规则的随意扩展

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

 

 

 

posted on 2018-12-21 11:21  胖胖滴加菲猫  阅读(824)  评论(0编辑  收藏  举报