一. 基本概念

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 : Attribute3     {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 : Attribute3     {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:Attribute6     {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)-反射

第十一节:特性(常见的特性标签、自定义特性、特性的使用案例)相关推荐

  1. 第四章第十一节数据资产盘点-设计数据资产标签

    第四章第十一节数据资产盘点-设计数据资产标签 在形成数据资产目录以后,开始设计数据资产标签体系,例如对于一个字段"客户名称"来说,需要给这个客户名称打上一个标签,比安全等级是几级? ...

  2. H5新增特性之语义化标签

    H5新增特性之语义化标签 语义化标签顾名思义标签有自己的含义,浏览器或者程序员一看就知道是什么.在HTML 5出来之前,我们用div来表示页面章节,但是这些div都没有实际意义.(即使我们用css样式 ...

  3. 【html】常见的行标签(inline)、块标签(block)和行块标签(inline-block)特点及相互转换

    常见的HTML标签 前言 一.行标签 1.span标签 2.a标签 3.i标签.b标签和em标签.strong标签 4.其他的行标签 5.行标签特点 二.块标签 1.div标签 2.标题(H1-H6) ...

  4. 【Python基础知识-pycharm版】第十一节-文件操作(IO技术)

    第十一节-文件操作(IO技术) 文本文件和二进制文件 文件操作相关模块概述 创建文件对象 open() 文本文件的写入 基本的文件写入操作 常用编码介绍 ASCII ISO8859-1 GB2312, ...

  5. 信管师培训之第十一节课作业(法律法规+标准规范+职业道德)

    一.法律法规和标准规范 1.中国标准划分为哪四个层次?要求最低的是哪个? <×××标准化法>将标准划分为4个层次:即国家标准.行业标准.地方标准和企业标准.其中国家标准要求最低. 2.国家 ...

  6. 大白话5分钟带你走进人工智能-第十一节梯度下降之手动实现梯度下降和随机梯度下降的代码(6)...

                                第十一节梯度下降之手动实现梯度下降和随机梯度下降的代码(6) 我们回忆一下,之前咱们讲什么了?梯度下降,那么梯度下降是一种什么算法呢?函数最优化 ...

  7. Python编程基础:第五十一节 将函数赋值给变量Assign Functions to Variables

    第五十一节 将函数赋值给变量Assign Functions to Variables 前言 实践 前言 简而言之,将函数赋值给变量就是为函数重命名,定义方式为新的函数名称=旧的函数名称,重命名后的新 ...

  8. Python编程基础:第三十一节 文件读取Read a File

    第三十一节 文件读取Read a File 前言 实践 前言 当我们检测到文件之后就可以读取其中的内容,读取所用到的函数是read(). 实践 我们依然以上一节的lyric.txt为例展示如何读取文件 ...

  9. Python编程基础:第二十一节 函数返回Return

    第二十一节 函数返回Return 前言 实践 前言 编程往往是用于实现某种计算并将计算结果进行返回,例如我们定义了一个函数用于计算两个数的和,那么最终的目的是将计算结果返回给用户.所以我们这里要进一步 ...

最新文章

  1. ALSA声卡笔记3--ASoC驱动重要结构体关系图
  2. 用webgl打造自己的3D迷宫游戏
  3. Session的模拟
  4. 计算矩阵的逆和行列式的值(高斯消元+LU分解)
  5. C++11 新特性 —— 关键字noexcept
  6. moba寻路_MOBA代号105:道具收费 加入自动寻路等MMO元素
  7. Linux里常见术语的缩写
  8. 【SPSS】包含多元线性回归、聚类分析、判别分析、主成分、相关系数、非参数秩检验的spss使用方法,含有相关例题,可以解决“数学建模”中数据建模的大部分问题
  9. URI和URL的区别比较与理解
  10. 大一linux考试试题及答案,大一计算机期末考试试题及答案
  11. 跟着团子学SAP PS:SAP PS模块常用报表介绍及增强建议
  12. 什么是短连接,如何用 Python 生成短连接?
  13. android加法计算器代码,Android实现简单加法计算器
  14. Apple not immune from viruses(苹果也不能免除病毒的侵害)
  15. JAVA小游戏有源代码,非常详细的注释,以及自己做的答辩PPT
  16. (Talking face) EVP
  17. python随机生成小写字母表_用小写字母生成大随机字符串的最快方法
  18. Functional ALV系列 (05) - ALV 作为数据编辑界面
  19. 专利检索工具网站分享
  20. 教妹学Java:接口,抽象的另一种表现方式

热门文章

  1. Unity3D 物体移动方法总结
  2. KVM--安装及初步使用
  3. Vijos——T 1629 八
  4. Flyweight Design Pattern 共享元设计模式
  5. fastdfs集群搭建2
  6. jQuery 请指出'$'和'$.fn'的区别?或者说出'$.fn'的用途。
  7. SQL 取n到m条记录
  8. html鼠标离开点击停留,Javascript DOM事件操作小结(监听鼠标点击、释放,悬停、离开等)...
  9. css没有border,你未必知道的CSS小知识:为什么没有人使用border-image
  10. 12v小型电机型号大全_鄂破碎机型号大全图,小型鄂破碎机价格