• C# 特性 (Attribute)


    C# 特性(Attribute)


    1.语法

    特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。

    说白了就是没有破坏类型封装的前提下,可以加点额外的信息和行为。

    声明

    实际上特性就是一个类,继承或者间接继承自Attribute
    如我们经常使用到的SerializableAttribute特性,就是继承自Attribute

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Delegate, Inherited = false)]
    [ComVisible(true)]
    public sealed class SerializableAttribute : Attribute//继承自Attribute 
    {
        public SerializableAttribute();
    }
    

    当然我们也是可以自定义一个特性的,它就像一个普通的类一样,有着自己的构造函数、属性、方法、字段等。

    在定义特性时,特性的名词我们一般是以Attribute结尾的。
    如下代码:

    public class CustomAttribute : Attribute
    {
    
        //无参数构造函数
        public CustomAttribute()
        {
    
        }
    
        //带有参数的构造函数
        public CustomAttribute(int id)
        {
    
        }
    
        //属性
        public string Name { get; set; }
    
        //字段
        public string Remark = "";
    }
    

    在使用特性时,只要在使用的元素上面加上[特性名称]即可。

    注意:

    • 每个元素可以添加多个特性
    • 如果特性时以Attribute结尾的话,是可以省略Attribute的
    • 特性可以添加参数

    如下代码

    [Serializable]
    [Custom]
    [CustomAttribute]
    [Custom()]//同 [Custom]
    [Custom(1)]//带有参数的构造函数
    [Custom(1, Name = "Oliver", Motto = "自律给我自由")]//直接指定成员
    public class Student
    {
        [Custom]//给属性添加特性
        public int Id { get; set; }
        public string Name { get; set; }
        public string Email { get; set; }
    
        [Custom]//给方法添加特性
        [return:Custom]//给方法的返回值添加特性
        public string Study([Custom]string content)//给方法的参数添加特性
        {
            return "我正在学习" + content;
        }
    }
    

    约束

    使用AttributeUsage 特性可以对自定义的特性进行约束,如:可以约束只能将特性添加到属性上等等,具体如下表所示:

    AttributeTargets 描述
    Assembly 程序集
    Module 模块
    Class
    Struct 结构;即,类型值
    Enum 枚举
    Constructor 构造函数
    Method 方法
    Property 属性
    Field 字段
    Event 事件
    Interface 接口
    Parameter 参数
    Delegate 委托
    ReturnValue 返回的值
    GenericParameter 泛型参数
    All 所有元素

    使用方式直接在特性类上面添加AttributeUsage特性。代码如下:

    //AttributeTargets:表示该特性可以应用于哪些程序元素的值。
    //AllowMultiple:该值指示是否可以为一个程序元素指定多个实例所指示的特性。
    //Inherited:该值确定指示的属性是否由派生类和重写成员继承。
    [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = true)]
    public class CustomAttribute : Attribute
    

    2.使用方法

    基本的语法我们都懂了,但是如何使用它呢?情看以下代码:

    使用类的特性

    Type studentType = typeof(Student);
    if (studentType.IsDefined(typeof(CustomAttribute), true))//判断该Type是否存在指定的特性
    {
        //获取到类的特性,然后可以根据自己的需求做一下应用
        CustomAttribute attributeType = (CustomAttribute)(studentType.GetCustomAttributes(typeof(CustomAttribute), true)[0]);
        Console.WriteLine($"attributeType.Name={attributeType.Name}
    attributeType.Motto={attributeType.Motto}");
    }
    //对应方法、字段、属性的特性的使用基本一致。
    

    可能有的人看了上面的代码一头雾水,使用自己定义的特性为何这么麻烦呢,还需要用到反射。见系统定义的特性直接拿来就用,不用做这么麻烦的事儿。在这里我说明下,只有是特性就需要有调用的地方,只是系统中的特性被封装了,而不用我们做这么麻烦的操作。

    下面就举两个例子来说明下特性:

    应用场景一

    场景:我们在开发过程中经常使用到枚举,然后再前台做相应的列表框,给用户显示相应的文字,然后再代码中使用枚举。

    可能我们给用户显示的是黄金会员,但是在代码中我们会使用MemberStatus.Gold,在前台页面显示时,我们难免会使用大量的if语句进行判断。在这里我们就可以使用特性解决这个问题。

    特性类

    我们定义一个特性类,专门用作存储枚举的备注信息。

    [AttributeUsage(AttributeTargets.Field)]//表示该特性只适用于字段(枚举中的项实际上是字段)
    public class RemarkAttribute : Attribute
    {
        private string _Remark = "";
        public RemarkAttribute(string remark)//构造函数
        {
            this._Remark = remark;
        }
    
        public string GetRemark()
        {
            return this._Remark;
        }
    }
    
    枚举类

    定义枚举,并在枚举的每个项上添加 RemarkAttribute特性

    /// <summary>
    /// 会员状态
    /// </summary>
    public enum MemberStatus
    {
        /// <summary>
        /// 普通会员
        /// </summary>
        [Remark("普通会员")]//给枚举类添加RemarkAttribute 特性
        Common = 0,
        /// <summary>
        /// 黄金会员
        /// </summary>
        [Remark("黄金会员")]
        Gold = 1,
        /// <summary>
        /// 钻石会员
        /// </summary>
        [Remark("钻石会员")]
        Diamond = 2
    }
    
    特性使用

    创建一个扩展类,用于获取枚举中特性的Remark信息

    /// <summary>
    /// 扩展类
    /// </summary>
    public static class RemarkExtend
    {
        /// <summary>
        /// 得到枚举类型的Remark信息
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string GetRemark(this Enum value)
        {
            Type type = value.GetType();
            FieldInfo fieldInfo= type.GetField(value.ToString());
            if (fieldInfo.IsDefined(typeof(RemarkAttribute), true))
            {
                RemarkAttribute remarkAttrbute = (RemarkAttribute)(fieldInfo.GetCustomAttributes(typeof(RemarkAttribute), true)[0]);
                return remarkAttrbute.GetRemark();
            }
            return value.ToString();
        }
    }
    
    调用方式

    直接调用枚举的扩展方法,从而得到枚举的备注信息

    Console.WriteLine(MemberStatus.Common.GetRemark());
    Console.WriteLine(MemberStatus.Gold.GetRemark());
    Console.WriteLine(MemberStatus.Diamond.GetRemark());
    
    /*
    输出:
    普通会员
    黄金会员
    钻石会员
    */
    

    应用场景二

    再举一个验证数据的例子。

    在我们开发的过程中经常需要把用户的输入的值,存入数据库,在存入数据库之前需要做相应的检查,这个问题我们也可以使用特性解决。

    比如我们有一个用户信息表如下:

    项目 需要验证
    姓名 长度为2~10个字符
    年龄 1~100
    展示信息 长度不超过100个字符
    特性类

    建立一个抽象类作为所有验证特性的父类,便于后期使用

    /// <summary>
    /// 验证的抽象特性类,所有验证特性都必须继承自它
    /// </summary>
    public abstract class ValidateAttribute : Attribute
    {
        /// <summary>
        /// 得到验证信息,做信息输出用
        /// </summary>
        /// <returns></returns>
        public abstract string GetValidateInfo();
        /// <summary>
        /// 验证所传入的值是否符合规范
        /// </summary>
        /// <param name="value">所验证的值</param>
        /// <returns>是否符合规范</returns>
        public abstract bool Validate(object value);
    }
    
    

    特性类1:用于验证长度是否合法

    /// <summary>
    /// 验证字符串长度的特性类
    /// </summary>
    public class ValidateLenAttribute : ValidateAttribute
    {
        /// <summary>
        /// 最大长度
        /// </summary>
        private int _Max;
        /// <summary>
        /// 最小长度
        /// </summary>
        private int _Min;
        public ValidateLenAttribute(int min, int max)
        {
            this._Max = max;
            this._Min = min;
        }
    
        public override string GetValidateInfo()
        {
            return string.Format("字符长度必须在[{0}-{1}]范围内。", this._Min, this._Max);
        }
    
        /// <summary>
        /// 验证所传入的值是否符合规范
        /// </summary>
        /// <param name="value">所验证的值</param>
        /// <returns>是否符合规范</returns>
        public override bool Validate(object value)
        {
            if (value != null)
            {
                int len = value.ToString().Length;
                if (len <= this._Max && len >= this._Min)
                {
                    return true;
                }
            }
            return false;
        }
    }
    
    

    特性类2,用于验证数字是否合法

    /// <summary>
    /// 验证数字范围的特性类
    /// </summary>
    public class ValidateIntAttribute : ValidateAttribute
    {
        private int _Max;
        private int _Min;
        public ValidateIntAttribute(int min, int max)
        {
            this._Max = max;
            this._Min = min;
        }
    
        /// <summary>
        /// 验证所传入的值是否符合规范
        /// </summary>
        /// <param name="value">所验证的值</param>
        /// <returns>是否符合规范</returns>
        public override bool Validate(object value)
        {
            if (!string.IsNullOrEmpty(value.ToString()) && !string.IsNullOrWhiteSpace(value.ToString()))
            {
                if (int.TryParse(value.ToString(), out int result))
                {
                    if (result <= this._Max && result >= this._Min)
                    {
                        return true;
                    }
                }
            }
            return false;
        }
    
        public override string GetValidateInfo()
        {
            return string.Format("数字必须在[{0}-{1}]范围内。", this._Min, this._Max);
        }
    }
    
    
    特性使用

    通过扩展方法使用特性类

    public static class ValidateExtend
    {
    
        /// <summary>
        /// 验证对象的属性是否符合规范
        /// </summary>
        /// <param name="obj">对象</param>
        /// <param name="errList">验证失败的错误信息</param>
        /// <returns>是否验证成功</returns>
        public static bool Validate(this object obj, out List<string> errList)
        {
            Type type = obj.GetType();//获取对象的类型
    
            errList = new List<string>();
    
            bool flag = true;
            foreach (PropertyInfo property in type.GetProperties())//得到该类型的所有属性,并循环
            {
                if (property.IsDefined(typeof(ValidateAttribute), true))//判断该属性是否 使用ValidateAttribute 特性
                {
                    object value = property.GetValue(obj);//得到该属性的值
                    foreach (ValidateAttribute validateAttribute in property.GetCustomAttributes(typeof(ValidateAttribute), true))//得到该属性所有的ValidateAttribute特性
                    {
                        if (!validateAttribute.Validate(value))//逐一言之是否符合规范
                        {
                            errList.Add(property.Name + "="+ value.ToString() + ",不满足验证条件:" + validateAttribute.GetValidateInfo());//拼接错误信息
                            flag = false;
                        }
                    }
                }
            }
            return flag;
        }
    }
    
    用户类

    在用户类中直接添加特性就可以完成验证,可以为一个属性添加多个验证特性,这样就可以双重验证(比如即验证数字范围,又验证字符串长度...)

    public class Member
    {
        [ValidateLenAttribute(2, 10)]
        public string Name { get; set; }
        [ValidateIntAttribute(1, 100)]
        public int Age { get; set; }
        [ValidateLenAttribute(0, 100)]
        public string Display { get; set; }
    }
    
    调用

    调用时很简单

    Member member1 = new Member() { Age = 18, Display = "自律给我自由", Name = "Oliver" };
    Console.WriteLine("member1验证结果:");
    if (!member1.Validate(out List<string> errList))
    {
        Console.WriteLine("验证失败:
    	" + string.Join("
    	", errList.ToArray()));
    }
    else
    {
        Console.WriteLine("验证通过");
    }
    Member member2 = new Member() { Age = 188, Display = "自律给我自由", Name = "OliverOliver" };
    Console.WriteLine("member2验证结果:");
    
    if (!member2.Validate(out List<string> errList2))
    {
        Console.WriteLine("验证失败:
    	" + string.Join("
    	", errList2.ToArray()));
    }
    else
    {
        Console.WriteLine("验证通过");
    }
    
    /* 
     输出:
        member1验证结果:
        验证通过
        member2验证结果:
        验证失败:
                Name=OliverOliver,不满足验证条件:字符长度必须在[2-10]范围内。
                Age=188,不满足验证条件:数字必须在[1-100]范围内。
     */
    
    
  • 相关阅读:
    OneNote2007产品密钥及激活方法
    穿着马甲的编码方式链式
    String 转换NameValueCollection 的方法 , 这是原创 嘎嘎
    TransactionScope分布式事务无法使用时, 配置并启动 MSDTC 的解决方案
    Spirit越狱3.1.3/3.2固件(Windows)
    智能感知 不正常的解决方法
    HttpHandler HttpModule入门篇
    Microsoft.Practices.Unity依赖注入使用实例
    ViewEngine 深入解析与应用实例
    打开chm文件提示“已取消到该网页的导航”的解决方案
  • 原文地址:https://www.cnblogs.com/haowuji/p/9449397.html
Copyright © 2020-2023  润新知