特性是fw1.0提出的,是原始一种语法。
什么是特性?
在类或方法或属性上使用的,形如:
[Obsolete("这是一个提示信息")]// 内置特性class Student{// 自定义特性[NonEmpty]public int Id { get; set; }public string Name { get; set; }public int Age { get; set; }}
使用的,叫做特性,特性是一个继承了Attribute抽象类的一个子类,这个类就是特性类,可以在特定的地方使用。
特性是一个类,可以用来标记元素,编译时生成medata,平时不影响程序的运行,除非主动的用反射查找,可以得到一些额外的信息/操作,然后扩展丰富的空间。
特性特点
特性可以影响程序的运行,特性是直接或间接继承attribute的子类一般声明特性的类的时候用Attribute结尾,而使用的时候可以省略。特性在默认情况下是不能重复修饰的。特性在编译后是metadata(元数据),所以只有反射可以使用。特性在声明之后是没用的,在程序运行过程中是没有影响的,除非用反射去找他,他才会使用。
修饰特性的特性
在.net Framwork框架中,内置了一个可以修饰特性的特性:
[AttributeUsage(AttributeTargets.All,AllowMultiple = false,Inherited = true)]class NonEmptyAttribute:Attribute{}
AttributeUsage这个特性就是用来修饰特性的,其中有三个字段,分别代表不同的意义:
AttributeTargets //设置特性使用的目标,如在类上、字段上、属性上等都能,是一个枚举类型,如果为All则代表在所有类型上都可以使用。AllowMultiple //代表是否可以重复标记,默认是不可以重复标记的,因为没有必要。为什么要在一个地方重复标记呢?Inherited //代表特性是否可以继承,默认是可以继承的,一个类如果继承了标有特性的类,这个特性也会被继承。
自定义特性
特性在MVC,ORM,API等等框架中都有使用,但是有的时候我们可以自定义自己的特性,实现自已的逻辑。
建造类并继承Attribute类
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace _06_Attribute.AttributeArgs{[AttributeUsage(AttributeTargets.All,AllowMultiple = false,Inherited = true)]class NonEmptyAttribute:Attribute{}}
再类名上最好用Attribute结尾,在使用的时候则不用加Attribute。这样我们就有了一个属于自己的特性。
使用特性
using _06_Attribute.AttributeArgs;using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace _06_Attribute.Models{[Obsolete("这是一个提示信息")]class Student{// 自定义特性[NonEmpty]public int Id { get; set; }public string Name { get; set; }public int Age { get; set; }}}
在需要的地方,满足特性的规则使用。
==注意:==到了这里其实都是没有什么用的,特性真正的使用方法是反射逻辑。
特性更像是一个flag。
反射调用特性
class CheckAttribute{public static void Check<T>(T t){// 反射泛型,获取typeType type = t.GetType();CheckType(type);}/// <summary>/// 检查类属性/// </summary>/// <param name="type"></param>/// <returns></returns>private static bool CheckType(Type type){// 查看类上是否有标记特定特性if (type.IsDefined(typeof(NonEmptyAttribute),true)){//找到特性object item = type.GetCustomAttributes(typeof(NonEmptyAttribute), true)[0];// 将特性转换,之后就是特性使用逻辑了Attribute attribute1 = item as Attribute;Console.WriteLine(attribute1);return true;}return false;}}
利用特性实现ORM中的特性验证
思想:
/// <summary>/// 属性检测类,这个检测类就看想象使用了,比如在ORM中,可以使用特性检测是否非空,字符长度等,都可以使用特性检测方法。/// 当然这个类是需要封装的,在什么地方调用合适??是否需要封装一个构造类?当调用一个构造方法时,使用这个类来构造,在构造完成后/// 调用属性检测方法,如果检测合格,则返回,若不合格则销毁?但是性能会有损耗吧,都构建出来了才检测,万一失败则销毁,这样会浪费构造的。/// </summary>
实现:
1、建立抽象父类
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace _06_Attribute.AttributeArgs{public abstract class BaseAttribute : Attribute{public abstract bool CheckAttribute(object t);}}
2、子类特性实现父类抽象
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace _06_Attribute.AttributeArgs{// 这是一个非空检测特性class RequirdAttribute : BaseAttribute{public override bool CheckAttribute(object tvalue){if (tvalue.ToString()=="0"||tvalue.ToString()=="null"){return false;}return true;}}}
3、特性验证类
using _06_Attribute.AttributeArgs;using System;using System.Collections.Generic;using System.Linq;using System.Reflection.Emit;using System.Text;using System.Threading.Tasks;namespace _06_Attribute{class CheckAttribute{public static string Check<T>(T t){// 反射泛型,获取typeType type = t.GetType();// 类检测if (!CheckType(type)){return "类型特性不符!!!";}// 属性检测if (!CheckProperties(type,t)){return "属性特性不符!!!";}// 检测字段if (!CheckFields(type)){return "字段特性不符!!!";}// 检测方法if(!CheckMethods(type)){return "方法特性不符!!!";}return "成功";}private static bool CheckMethods(Type type){foreach (var item in type.GetMethods()){if (item.IsDefined(typeof(BaseAttribute), true)){object v = item.GetCustomAttributes(typeof(BaseAttribute), true)[0];BaseAttribute attribute = v as BaseAttribute;if (attribute.CheckAttribute(item)){return true;}else{return false;}}return true;}return true;}private static bool CheckFields(Type type){foreach (var item in type.GetFields()){if (item.IsDefined(typeof(BaseAttribute), true)){object v = item.GetCustomAttributes(typeof(BaseAttribute), true)[0];BaseAttribute attribute = v as BaseAttribute;Console.WriteLine(item.GetValue(type));if (attribute.CheckAttribute(item.GetValue(type))){return true;}else{return false;}}return true;}return true;}private static bool CheckProperties(Type type,object t){foreach (var item in type.GetProperties()){if (item.IsDefined(typeof(BaseAttribute), true)){object v = item.GetCustomAttributes(typeof(BaseAttribute), true)[0];BaseAttribute attribute = v as BaseAttribute;//Console.WriteLine(item.GetValue(t));if (attribute.CheckAttribute(item.GetValue(t))){return true;}else{return false;}}return true;}return true;}/// <summary>/// 检查类属性/// </summary>/// <param name="type"></param>/// <returns></returns>private static bool CheckType(Type type){// 查看类上是否有标记特定特性if (type.IsDefined(typeof(BaseAttribute),true)){//找到特性object item = type.GetCustomAttributes(typeof(BaseAttribute), true)[0];// 将特性转换,之后就是特性使用逻辑了BaseAttribute attribute = item as BaseAttribute;if (attribute.CheckAttribute(type)){return true;}else{return false;}}return true;}}}
4、使用调用
Student student = new Student() {Age=15 };string v = CheckAttribute.Check<Student>(student);Console.WriteLine(v);
总结:
特用并不难,只是需要一种思想,特性有类似于AOP的思想,就是在不破坏原有代码结构的基础上,可以增加一些逻辑扩展,但是还是有一些局限性的。要多多使用。
