特性是fw1.0提出的,是原始一种语法。

什么是特性?

在类或方法或属性上使用的,形如:

  1. [Obsolete("这是一个提示信息")]// 内置特性
  2. class Student
  3. {
  4. // 自定义特性
  5. [NonEmpty]
  6. public int Id { get; set; }
  7. public string Name { get; set; }
  8. public int Age { get; set; }
  9. }

使用的,叫做特性,特性是一个继承了Attribute抽象类的一个子类,这个类就是特性类,可以在特定的地方使用。

特性是一个类,可以用来标记元素,编译时生成medata,平时不影响程序的运行,除非主动的用反射查找,可以得到一些额外的信息/操作,然后扩展丰富的空间。

特性特点

特性可以影响程序的运行,特性是直接或间接继承attribute的子类一般声明特性的类的时候用Attribute结尾,而使用的时候可以省略。特性在默认情况下是不能重复修饰的。特性在编译后是metadata(元数据),所以只有反射可以使用。特性在声明之后是没用的,在程序运行过程中是没有影响的,除非用反射去找他,他才会使用。

修饰特性的特性

在.net Framwork框架中,内置了一个可以修饰特性的特性:

  1. [AttributeUsage(AttributeTargets.All,AllowMultiple = false,Inherited = true)]
  2. class NonEmptyAttribute:Attribute
  3. {
  4. }

AttributeUsage这个特性就是用来修饰特性的,其中有三个字段,分别代表不同的意义:

  1. AttributeTargets //设置特性使用的目标,如在类上、字段上、属性上等都能,是一个枚举类型,如果为All则代表在所有类型上都可以使用。
  2. AllowMultiple //代表是否可以重复标记,默认是不可以重复标记的,因为没有必要。为什么要在一个地方重复标记呢?
  3. Inherited //代表特性是否可以继承,默认是可以继承的,一个类如果继承了标有特性的类,这个特性也会被继承。

自定义特性

特性在MVC,ORM,API等等框架中都有使用,但是有的时候我们可以自定义自己的特性,实现自已的逻辑。

建造类并继承Attribute类

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. namespace _06_Attribute.AttributeArgs
  7. {
  8. [AttributeUsage(AttributeTargets.All,AllowMultiple = false,Inherited = true)]
  9. class NonEmptyAttribute:Attribute
  10. {
  11. }
  12. }

再类名上最好用Attribute结尾,在使用的时候则不用加Attribute。这样我们就有了一个属于自己的特性。

使用特性

  1. using _06_Attribute.AttributeArgs;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. namespace _06_Attribute.Models
  8. {
  9. [Obsolete("这是一个提示信息")]
  10. class Student
  11. {
  12. // 自定义特性
  13. [NonEmpty]
  14. public int Id { get; set; }
  15. public string Name { get; set; }
  16. public int Age { get; set; }
  17. }
  18. }

在需要的地方,满足特性的规则使用。

==注意:==到了这里其实都是没有什么用的,特性真正的使用方法是反射逻辑。

特性更像是一个flag。

反射调用特性

  1. class CheckAttribute
  2. {
  3. public static void Check<T>(T t)
  4. {
  5. // 反射泛型,获取type
  6. Type type = t.GetType();
  7. CheckType(type);
  8. }
  9. /// <summary>
  10. /// 检查类属性
  11. /// </summary>
  12. /// <param name="type"></param>
  13. /// <returns></returns>
  14. private static bool CheckType(Type type)
  15. {
  16. // 查看类上是否有标记特定特性
  17. if (type.IsDefined(typeof(NonEmptyAttribute),true))
  18. {
  19. //找到特性
  20. object item = type.GetCustomAttributes(typeof(NonEmptyAttribute), true)[0];
  21. // 将特性转换,之后就是特性使用逻辑了
  22. Attribute attribute1 = item as Attribute;
  23. Console.WriteLine(attribute1);
  24. return true;
  25. }
  26. return false;
  27. }
  28. }

利用特性实现ORM中的特性验证

思想:

  1. /// <summary>
  2. /// 属性检测类,这个检测类就看想象使用了,比如在ORM中,可以使用特性检测是否非空,字符长度等,都可以使用特性检测方法。
  3. /// 当然这个类是需要封装的,在什么地方调用合适??是否需要封装一个构造类?当调用一个构造方法时,使用这个类来构造,在构造完成后
  4. /// 调用属性检测方法,如果检测合格,则返回,若不合格则销毁?但是性能会有损耗吧,都构建出来了才检测,万一失败则销毁,这样会浪费构造的。
  5. /// </summary>

实现:

1、建立抽象父类

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. namespace _06_Attribute.AttributeArgs
  7. {
  8. public abstract class BaseAttribute : Attribute
  9. {
  10. public abstract bool CheckAttribute(object t);
  11. }
  12. }

2、子类特性实现父类抽象

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. namespace _06_Attribute.AttributeArgs
  7. {
  8. // 这是一个非空检测特性
  9. class RequirdAttribute : BaseAttribute
  10. {
  11. public override bool CheckAttribute(object tvalue)
  12. {
  13. if (tvalue.ToString()=="0"||tvalue.ToString()=="null")
  14. {
  15. return false;
  16. }
  17. return true;
  18. }
  19. }
  20. }

3、特性验证类

  1. using _06_Attribute.AttributeArgs;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Reflection.Emit;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. namespace _06_Attribute
  9. {
  10. class CheckAttribute
  11. {
  12. public static string Check<T>(T t)
  13. {
  14. // 反射泛型,获取type
  15. Type type = t.GetType();
  16. // 类检测
  17. if (!CheckType(type))
  18. {
  19. return "类型特性不符!!!";
  20. }
  21. // 属性检测
  22. if (!CheckProperties(type,t))
  23. {
  24. return "属性特性不符!!!";
  25. }
  26. // 检测字段
  27. if (!CheckFields(type))
  28. {
  29. return "字段特性不符!!!";
  30. }
  31. // 检测方法
  32. if(!CheckMethods(type))
  33. {
  34. return "方法特性不符!!!";
  35. }
  36. return "成功";
  37. }
  38. private static bool CheckMethods(Type type)
  39. {
  40. foreach (var item in type.GetMethods())
  41. {
  42. if (item.IsDefined(typeof(BaseAttribute), true))
  43. {
  44. object v = item.GetCustomAttributes(typeof(BaseAttribute), true)[0];
  45. BaseAttribute attribute = v as BaseAttribute;
  46. if (attribute.CheckAttribute(item))
  47. {
  48. return true;
  49. }
  50. else
  51. {
  52. return false;
  53. }
  54. }
  55. return true;
  56. }
  57. return true;
  58. }
  59. private static bool CheckFields(Type type)
  60. {
  61. foreach (var item in type.GetFields())
  62. {
  63. if (item.IsDefined(typeof(BaseAttribute), true))
  64. {
  65. object v = item.GetCustomAttributes(typeof(BaseAttribute), true)[0];
  66. BaseAttribute attribute = v as BaseAttribute;
  67. Console.WriteLine(item.GetValue(type));
  68. if (attribute.CheckAttribute(item.GetValue(type)))
  69. {
  70. return true;
  71. }
  72. else
  73. {
  74. return false;
  75. }
  76. }
  77. return true;
  78. }
  79. return true;
  80. }
  81. private static bool CheckProperties(Type type,object t)
  82. {
  83. foreach (var item in type.GetProperties())
  84. {
  85. if (item.IsDefined(typeof(BaseAttribute), true))
  86. {
  87. object v = item.GetCustomAttributes(typeof(BaseAttribute), true)[0];
  88. BaseAttribute attribute = v as BaseAttribute;
  89. //Console.WriteLine(item.GetValue(t));
  90. if (attribute.CheckAttribute(item.GetValue(t)))
  91. {
  92. return true;
  93. }
  94. else
  95. {
  96. return false;
  97. }
  98. }
  99. return true;
  100. }
  101. return true;
  102. }
  103. /// <summary>
  104. /// 检查类属性
  105. /// </summary>
  106. /// <param name="type"></param>
  107. /// <returns></returns>
  108. private static bool CheckType(Type type)
  109. {
  110. // 查看类上是否有标记特定特性
  111. if (type.IsDefined(typeof(BaseAttribute),true))
  112. {
  113. //找到特性
  114. object item = type.GetCustomAttributes(typeof(BaseAttribute), true)[0];
  115. // 将特性转换,之后就是特性使用逻辑了
  116. BaseAttribute attribute = item as BaseAttribute;
  117. if (attribute.CheckAttribute(type))
  118. {
  119. return true;
  120. }
  121. else
  122. {
  123. return false;
  124. }
  125. }
  126. return true;
  127. }
  128. }
  129. }

4、使用调用

  1. Student student = new Student() {Age=15 };
  2. string v = CheckAttribute.Check<Student>(student);
  3. Console.WriteLine(v);

总结:

特用并不难,只是需要一种思想,特性有类似于AOP的思想,就是在不破坏原有代码结构的基础上,可以增加一些逻辑扩展,但是还是有一些局限性的。要多多使用。