zoukankan      html  css  js  c++  java
  • 第十一节:特性(常见的特性标签、自定义特性、特性的使用案例)

    一. 基本概念

    1. 什么是特性?

     MSDN官方给出的定义时:公共语言运行时允许添加类似关键字的描述声明,叫做特性,它对程序中的元素进行标注,如类型、字段、方法和属性等。Attribute和Microsoft .Net Framework文件的元数据(metadata)保存在一起,可以用来向运行时描述你的代码,或者在程序运行时影响程序的行为。 

        我的理解:在不影响类封装的情况的下,额外的添加一些信息,如果你用这个信息,则特性有效;如果你不用这个信息,那么这个特性无效。我们通常使用反射的方式获取类、属性、或方法等上面标注的特性。

    2. 分清三个概念

       (1). 注释:写在代码上面,一般用“ // ”和“ /**/ ”两个符号来表示,用来说明代码段或代码块的含义,方便自己或别人理解,对代码运行没有任何影响。

       (2). 属性:属性和特性虽然一字之差,但是完全两个不同的概念,属性在面向对象中,提供了私有或字段的封装,可以通过get和set访问器来设置可读可写。

       (3). 特性:不影响类的封装,在运行时,可以通过反射获取特性的内容。

    3. DotNet中常用特性

       (1). [Serializable]和[NonSerialized] :表示可以序列化或不可以序列化。

       (2). [Obsolete("该类不能用了",true)]:表示该类(或属性或字段)将不能被使用。

       (3). [AttributeUsage]:用来限制特性的作用范围、是否允许多次修饰同一个对象、是否允许被继承。

       (4). [ReadOnly(true)]: 表示该特性作用于的属性为只读属性。

       (5). [Description("XXX")]:用来描述作用对象含义的。

       (6). [Flags]: 指示可以将枚举作为位域(即一组标志)处理

       (7). [DllImport("")]   : 使用包含要导入的方法的 DLL 的名称初始化

    二. 自定义特性

    1. 可作用的范围?

       程序集(assembly)、模块(module)、类型(type)、属性(property)、事件(event)、字段(field)、方法(method)、参数(param)、返回值(return)。

    2. 约定规则?

      (1). 声明以Attribute结尾的类,即xxx+Attribute。

      (2). 必须继承或间接继承Attribute类。

      (3). 必须要有公有的构造函数。

    3. 特性的使用方式(eg: ypfAttribute特性  mrAttribute特性)

      (1). 可以省略后缀,也可以不省略。  eg:[ypfAttribute]、[ypf]、[ypfAttribute()]、[ypf()]  。

      (2). 多个特性共同作用于一个对象的使用方式: [ypfAttribute][mrAttribute]、 [ypfAttribute,mrAttribute]  (注:也可以省略后缀的各种组合形式)

    4. 特性的构建

      (1). 特性中除了可以声明构造函数,还可以声明属性和字段。(方法是不可以的)

      (2). 可以通过DotNet自带的特性来限制自定义的特性。 [AttributeUsage(AttributeTargets.All,AllowMultiple =true,Inherited =false)]

      a:AttributeTargets.All 表示可以加在所有的上面(包括类、属性、接口),也可以根据自己的需求,比如 AttributeTargets.Class 只允许加在类上。

      b:约束该特性能否同时作用于某个元素(类、方法、属性等等)多次,默认为false。

      c:  约束该特性作用于基类(或其它)上,其子类能否继承该特性,默认为true。

     5. 下面自定义一个ypf特性

     1     [AttributeUsage(AttributeTargets.All,AllowMultiple =true,Inherited =false)]
     2     public class ypfAttribute : Attribute
     3     {
     4         /// <summary>
     5         /// 默认的构造函数
     6         /// </summary>
     7         public ypfAttribute()
     8         {
     9 
    10         }
    11 
    12         /// <summary>
    13         /// 新声明的构造函数
    14         /// </summary>
    15         public ypfAttribute(int id)
    16         {
    17 
    18         }
    19         /// <summary>
    20         /// 新声明的构造函数
    21         /// </summary>
    22         public ypfAttribute(int id,string name)
    23         {
    24 
    25         }
    26         /// <summary>
    27         /// 声明要给属性
    28         /// </summary>
    29         public string Remark { get; set; }
    30 
    31         /// <summary>
    32         /// 声明一个字段
    33         /// </summary>
    34         public string Description = null;
    35     }

     (1).  作用形式

     1     /// <summary>
     2     /// 用户实体类
     3     /// </summary>
     5     [ypfAttribute]
     6     [ypf]
     7     [ypfAttribute()]
     8     [ypf()]    //以上四个等价
     9     [ypf(123)]
    10     [ypfAttribute(123)]
    11     [ypf(123, "456")]
    12     [ypfAttribute(123, "456")]
    13     [ypf(Remark = "这里是特性")]
    14     [ypf(123, Remark = "这里是特性")]
    15     [ypfAttribute(123, Remark = "这里是特性")]
    16     [ypf(123, "456", Remark = "这里是特性")]
    17     [ypfAttribute(123, "456", Remark = "这里是特性", Description = "Description")]
    18 
    19     [Table("User")]
    20     public class UserModel
    21     {
    22         /// <summary>
    23         /// 主键ID
    24         /// </summary>
    25         [myValidate(1, 1000)]
    26         public int Id { get; set; }
    27         /// <summary>
    28         /// 账号
    29         /// </summary>
    30         public string Account { get; set; }
    31         /// <summary>
    32         /// 密码
    33         /// </summary>
    34         public string Password { get; set; }
    35         /// <summary>
    36         /// EMaill
    37         /// </summary>
    38         public string Email { get; set; }
    39 
    40     }

    (2). 如何获取特性中值? (   [ypfAttribute(123, "456", Remark = "这里是特性", Description = "Description")]  )

    A.  重新构建ypfAttribute中的内容,需要在该特性内部封装四个获取四个内容的方法
     1  [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
     2     public class ypfAttribute : Attribute
     3     {
     4         public int _id;
     5         public string _name;
     6 
     7         /// <summary>
     8         /// 默认的构造函数
     9         /// </summary>
    10         public ypfAttribute()
    11         {
    12 
    13         }
    14 
    15         /// <summary>
    16         /// 新声明的构造函数
    17         /// </summary>
    18         public ypfAttribute(int id)
    19         {
    20 
    21         }
    22         /// <summary>
    23         /// 新声明的构造函数
    24         /// </summary>
    25         public ypfAttribute(int id, string name)
    26         {
    27             this._id = id;
    28             this._name = name;
    29         }
    30         /// <summary>
    31         /// 声明要给属性
    32         /// </summary>
    33         public string Remark { get; set; }
    34 
    35         /// <summary>
    36         /// 声明一个字段
    37         /// </summary>
    38         public string Description = null;
    39 
    40         //下面封装四个方法,分别获取该属性中的内容
    41         public int GetOwnId()
    42         {
    43             return this._id;
    44         }
    45         public string GetOwnName()
    46         {
    47             return this._name;
    48         }
    49         public string GetOwnRemark()
    50         {
    51             return this.Remark;
    52         }
    53         public string GetOwnDescription()
    54         {
    55             return this.Description;
    56         }
    57     }

     B:封装获取特性内容的可供外部调用的方法(在该方法中要调用ypf内部的封装的方法)

    
    
     1          /// <summary>
     2         /// 根据类型获取自定义特性ypfAttribute中的四个内容
     3         /// </summary>
     4         /// <typeparam name="T"></typeparam>
     5         /// <param name="t"></param>
     6         public static void GetypfAttributeMsg<T>(this T t) where T : new()
     7         {
     8             //1.获取类
     9             Type type = t.GetType();
    10             //2. 获取类中的所有特性
    11             object[] attributeList = type.GetCustomAttributes(true);
    12             //3. 查找ypf特性
    13             foreach (var item in attributeList)
    14             {
    15                 if (item is ypfAttribute)
    16                 {
    17                     ypfAttribute ypfattr = item as ypfAttribute;
    18                     int id = ypfattr.GetOwnId();
    19                     string Name = ypfattr.GetOwnName();
    20                     string remark = ypfattr.GetOwnRemark();
    21                     string Des = ypfattr.GetOwnDescription();
    22                     Console.WriteLine("ypfAttribute上的四个内容分别为:{0},{1},{2},{3}",id,Name,remark,Des);
    23                 }
    24             }
    25         }
    C. 调用
    1  {
    2                 //测试获取UserModel类上的ypfAttribute中的四个内容
    3                 Console.WriteLine("----------------------测试获取UserModel类上的ypfAttribute中的四个内容--------------------");
    4                 UserModel userModel = new UserModel();
    5                 userModel.GetypfAttributeMsg();
    6   }

    D. 结果

    三. 案例(制作一个验证属性长度的特性)

    1. 构建一个myValidateAttribute特性,里面包含校验方法。

     1   /// <summary>
     2     /// 验证int类型属性长度的特性
     3     /// </summary>
     4     [AttributeUsage(AttributeTargets.Property)]   //表示该特性只能作用于属性上
     5     public class myValidateAttribute:Attribute
     6     {
     7         private int _min = 0;
     8         private int _max = 0;
     9 
    10         /// <summary>
    11         /// 自定义构造函数
    12         /// </summary>
    13         /// <param name="min"></param>
    14         /// <param name="max"></param>
    15         public myValidateAttribute(int min,int max)
    16         {
    17             this._min = min;
    18             this._max = max;
    19         }
    20         /// <summary>
    21         /// 封装校验是否合法的方法
    22         /// </summary>
    23         /// <param name="num"></param>
    24         /// <returns></returns>
    25         public bool CheckIsRational(int num)
    26         {
    27             return num >= this._min && num <= this._max; 
    28         }
    29     }

    2. 外部校验方法

     1         /// <summary>
     2         /// 校验并保存的方法
     3         /// </summary>
     4         /// <typeparam name="T"></typeparam>
     5         /// <param name="t"></param>
     6         public static void Save<T>(T t)
     7         {
     8             bool isSafe = true;
     9             //1. 获取实例t所在的类
    10             Type type = t.GetType();
    11             //2. type.GetProperties() 获取类中的所有属性
    12             foreach (var property in type.GetProperties())
    13             {
    14                 //3. 获取该属性上的所有特性
    15                 object[] attributesList = property.GetCustomAttributes(true);
    16                 //4. 找属性中的特性
    17                 foreach (var item in attributesList)
    18                 {
    19                     if (item is myValidateAttribute)
    20                     {
    21                         myValidateAttribute attribute = item as myValidateAttribute;
    22                         //调用特性中的校验方法
    23                         //表示获取属性的值:property.GetValue(t)
    24                         isSafe = attribute.CheckIsRational((int)property.GetValue(t));
    25                     }
    26                 }
    27                 if (!isSafe)
    28                 {
    29                     break;
    30                 }
    31             }
    32             if (isSafe)
    33             {
    34                 Console.WriteLine("保存到数据库");
    35             }
    36             else
    37             {
    38                 Console.WriteLine("数据不合法");
    39             }
    40         }

    3. 调用

    1  {
    2                 //制作一个可以限制int类型属性长度的特性,并封装对应的校验方法
    3                 UserModel userModel = new UserModel();
    4                 // userModel.Id = 1000;
    5                 userModel.Id = 1001;  // 不合法
    6                 BaseDal.Save<UserModel>(userModel);
    7  }

    4. 结果

    四. 总结

      自定义特性的使用步骤: 声明特性→特性中封装获取特性参数的方法→将特性作用于对象上→封装外部校验作用对象的方法→调用

      封装外部校验作用对象的方法要用到反射,这里简单补充一下反射在知识:

       反射详见章节:    .Net进阶系列(2)-反射

  • 相关阅读:
    七夜在线音乐台开发 第二弹 (原创)
    七夜在线音乐台开发 第一弹 (原创)
    逆向某停车app(原创)
    (原创)python发送邮件
    申论(写作篇)之“人民时评”帮你写好作文
    行测(爆发篇)之片段阅读,不能只靠语感
    行测(爆发篇)之逻辑填空
    申论(写作篇)之文字表达能力提升
    申论(写作篇)之花木体申论写作法
    公考笔记整理(全)
  • 原文地址:https://www.cnblogs.com/yaopengfei/p/7088086.html
Copyright © 2011-2022 走看看