特性

特性

特性无处不在,在MVC、WCF、ORM、EF等等中都使用到了特性。

比如【HttpGet】、【HttpPost】、【Table】

特性有两个作用:提供额外信息,提供额外功能

特性必须使用反射才能调用

基于IL解读特性原理

什么是特性?

  1. 增加了特性以后,就可以有一些厉害的功能,甚至可以影响编译器
  2. 特性就可以增加一些功能

比如系统提供的特性Obsolete,用以报出警告或异常;Serializable用以表示可以序列化与反序列化。

在中间语言IL中,我们发现标记了特性的元素内部都会生成一个.custom,但是在C#无法调用

特性初步探索

特性的本质

特性的本质就是一个类

特性的声明和使用
  1. 声明一个类,直接或间接继承Attribute类(这个类的名字约定俗成以Attribute结尾)

  2. 这个时候就可以在其他类、属性、字段、方法、方法参数、方法返回值等上进行标记,使用英文中括号包裹

  3. 如果是以Attribute结尾的话,尾部的Attribute在标记使用时可以省略

AttributeUsage

​ 可以影响编译器,指定当前特性可以标记的位置、能否重复修饰、是否可以继承

​ 也是一个特性

​ 标记在特性上的特性

特性的生效

​ 需要使用到反射

特性提供额外信息

比如,获取枚举值

  1. 首先创建特性

    [AttributeUsage(AttributeTargets.Field)]
    public class RemarkAttribute : Attribute
    {
        public string Remark { get; private set; }
    
        public RemarkAttribute(string remark)
        {
            this.Remark = remark;
        }
    }
    
  1. 创建枚举并使用特性

    public enum UserStuta
    {
        /// <summary>
        /// 正常状态
        /// </summary>
        [RemarkAttribute("正常")]
        Normal = 0,
        /// <summary>
        /// 已冻结
        /// </summary
        [RemarkAttribute("已冻结")]
        Frozen = 1,
        /// <summary>
        /// 已删除
        /// </summary>
        [RemarkAttribute("已删除")]
        Deleted = 2
    }
    
  1. 使用反射获取特性值

    public static string GetRemark(this Enum @enum) 
    {
        Type type = @enum.GetType();
        FieldInfo? filedInfo = type.GetField(@enum.ToString());
        if (filedInfo != null)
        {
            if (filedInfo.IsDefined(typeof(RemarkAttribute), true))
            {
                RemarkAttribute remarkAttribute = (RemarkAttribute)filedInfo.GetCustomAttribute(typeof(RemarkAttribute), true);
                return remarkAttribute.Remark;
            }
        }
        return @enum.ToString();
    }
    
    • 注意这里使用反射获取特性时,首先需要判断该元素是否标记了特性,使用方法【某元素的Info】.IsDefined(typeof(特性类型), 是否包含继承),例如上述代码中的filedInfo.IsDefined(typeof(RemarkAttribute), true)

    • 然后使用GetCustomAttribute方法得到特性的实例,然后返回特性中的属性/字段。(这里可以使用泛型的GetCustomeAttribute<特性的类型>(是否包含继承)更简便)

    • 如果特性允许多个标记,且相关元素确实标记了多个该特性,那么在使用GetCustomAttribute就肯定会报错了,因为它找到了多个!!!所以这个时候应该使用fileInfo.GetCustomAttributes<RemarkAttribute>(true);,然后循环得到的特性List

特性提供额外功能

比如,验证数据信息(手机号、邮箱、密码、账户是否符合规范)

特性提供额外功能和特性提供额外信息类似,只是在特性中把属性/字段的读写改成对方法的调用。

比如,验证手机号是否符合长度时:

  1. 首先定义特性

    [AttributeUsage(AttributeTargets.Property)]
        public class MobileNumAtrribute : AbstractValidateAttribute
        {
            public int Count { get; private set; }
    
            public MobileNumAtrribute(int count)
            {
                if (count<=0)
                {
                    throw new Exception("手机号长度不可能为负数");
                }
                Count = count;
            }
    
            public override bool Validate(object mobileNum)
            { 
                return mobileNum != null && mobileNum.ToString().Length == Count;
            }
    
        }
    
  1. 执行验证的方法

    public static bool Validate<T>(T t)
            { 
                Type type = t.GetType();
                foreach (PropertyInfo prop in type.GetProperties())
                {
                    if (prop.IsDefined(typeof(AbstractValidateAttribute), true))
                    {
                        object oValue = prop.GetValue(t);
                        AbstractValidateAttribute atrribute = prop.GetCustomAttribute<AbstractValidateAttribute>(true);
                        if (!atrribute.Validate(oValue))
                        {
                            return false;
                        }
                    }
    
                }
                return true;
            }
    
  1. 调用方法即可
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容