C#高级开发之 特性(Attribute)三
一、什么是特性
特性:特性实际是一个类 直接/间接继承Attribute类 命名一般以Attribute结尾,用中括号([])标注申明该对象且一般省略Attribute。添加特性后,编译后元素会在IL内部产生,而且在metadata里会有记录(实际上会产生对应的构造函数从IL中可以查看),所以必须通过反射才能查到或获取对应信息(反射读取metadata信息)。
特性是额外附带的一些信息不主动调用它对程序并不产生相应功能,需要主动调用。系统里的特性是程序运行的时候已经调用好了且查找过所以可以直接标注就能用,自定义的特性就必须得主动去调用。
二、自定义特性
C#中除了系统自带的特性外我们可以自己定义一些特性。所有自定义的Attribute必须从Attribute类派生(直接或间接继承Attribute类),命名一般以Attribute结尾,在使用的时候可以省略Attribute。特性在没有破坏类型封装的前提下,可以加点额外的信息和行为。特性 本身是没啥用处的,必须主动使用,程序运行的过程中,我们通过反射能找到,这样就可以用了。
1. 特性的声明及使用
1 namespace MyAttribute 2 { 3 public class CustomAttribute : Attribute//必须直接或间接继承Attribute类 4 { 5 public CustomAttribute() 6 { 7 8 } 9 public CustomAttribute(int i) 10 { 11 } 12 public string Description { get; set; } 13 public string Remark = null; 14 public void show() 15 { 16 Console.WriteLine($"this is{nameof(CustomAttribute)}"); 17 } 18 public void login() 19 { 20 Console.WriteLine($" this is{nameof(CustomAttribute)}登陆了"); 21 } 22 } 23 }
在user类中标记特性
1 namespace MyAttribute 2 { 3 [Custom(123,Remark="总监",Description ="系统用户")]//修饰类,类似实例化一个类,指定调用的构造方法,指定类的属性,字段等 4 public class User 5 { 6 public string Name { set; get; } 7 8 public string Account { set; get; } 9 [Leng(2, 10)] 10 public string Password { set; get; } 11 12 public string Email { set; get; } 13 14 public string Mobile { set; get; } 15 16 public int CompanyId { set; get; } 17 [Custom(Remark = "张氏集团", Description = "系统用户")]//类似实例化一个类,调用相对应的构造函数 18 public string CompanyName { set; get; } 19 20 /// <summary> 21 /// 用户状态 0正常 1冻结 2删除 22 /// </summary> 23 [Custom(Remark = "正常")] 24 public int State { set; get; } 25 26 /// <summary> 27 /// 用户类型 1 普通用户 2管理员 4超级管理员 28 /// </summary> 29 public int UserType { set; get; } 30 31 public DateTime LastLoginTime { set; get; } 32 33 public DateTime CreateTime { set; get; } 34 35 public int CreatorId { set; get; } 36 37 public int LastModifierId { set; get; } 38 39 public DateTime LastModifyTime { set; get; } 40 [Custom(Remark = "总监", Description = "系统用户")]//类似实例化一个类,默认调用无惨构造函数 41 public void login([Custom(Remark ="123")]string name )//给方法的参数修饰 42 { 43 Console.WriteLine($"this is{name}"); 44 } 45 public void logout() 46 { 47 Console.WriteLine("this is logout"); 48 } 49 } 50 }
三、反射查找类及类的成员上标注的特性
1 public static void Show(User user) 2 { 3 //通过反射访问类上标注的特性 4 Type type = user.GetType(); 5 if (type.IsDefined(typeof(CustomAttribute), true))//检测某一类型是否有该特性 6 { 7 //通过反射查找特性实际上是在调用构造函数创建对象 8 CustomAttribute customAttribute=(CustomAttribute)type.GetCustomAttribute(typeof(CustomAttribute), true); 9 Console.WriteLine($"{customAttribute.Remark}_{customAttribute.Description}"); 10 //user.login(); 11 } 12 //通过反射访问属性上标注的特性 13 PropertyInfo propertyInfo = type.GetProperty("Name"); 14 if (propertyInfo.IsDefined(typeof(CustomAttribute), true))//检测某一类型是否有该特性 15 { 16 //通过反射查找特性实际上是在调用构造函数创建对象 17 CustomAttribute customAttribute = (CustomAttribute)propertyInfo.GetCustomAttribute(typeof(CustomAttribute), true); 18 Console.WriteLine($"{customAttribute.Remark}_{customAttribute.Description}"); 19 //user.login(); 20 } 21 //通过反射访问方法上标注的特性 22 MethodInfo methodInfo = type.GetMethod("login"); 23 if (methodInfo.IsDefined(typeof(CustomAttribute), true))//检测某一类型是否有该特性 24 { 25 //通过反射查找特性实际上是在调用构造函数创建对象 26 CustomAttribute customAttribute = (CustomAttribute)methodInfo.GetCustomAttribute(typeof(CustomAttribute), true); 27 Console.WriteLine($"{customAttribute.Remark}_{customAttribute.Description}"); 28 //user.login(); 29 } 30 //通过反射访问方法参数上标注的特性 31 ParameterInfo parameterInfo = methodInfo.GetParameters()[0]; 32 if (parameterInfo.IsDefined(typeof(CustomAttribute), true))//检测某一类型是否有该特性 33 { 34 //通过反射查找特性实际上是在调用构造函数创建对象 35 CustomAttribute customAttribute = (CustomAttribute)parameterInfo.GetCustomAttribute(typeof(CustomAttribute), true); 36 Console.WriteLine($"{customAttribute.Remark}_{customAttribute.Description}"); 37 //user.login(); 38 } 39 user.login("张三"); 40 }
四、特性的运用(额外增加信息和行为)
给字段或是属性添加额外的信息 本示例是给枚举类型扩展一个方法来读取添加的额外信息
1 public enum UserState 2 { 3 /// <summary> 4 /// 正常 5 /// </summary> 6 [Remark("正常")] 7 Normal = 0, 8 /// <summary> 9 /// 冻结 10 /// </summary> 11 [Remark("冻结")] 12 Frozen = 1, 13 /// <summary> 14 /// 15 /// </summary> 16 [Remark("删除")] 17 Deleted = 2 18 19 } 20 /// <summary> 21 /// 自定义特性 获取传入参数的值 22 /// </summary> 23 public class RemarkAttribute : Attribute 24 { 25 private string _remark = null; 26 public RemarkAttribute(string remark) 27 { 28 this._remark = remark; 29 } 30 public string GetRemark() 31 { 32 return this._remark; 33 } 34 } 35 /// <summary> 36 /// 枚举扩展方法 静态类+静态方法 然后参数前面加个this 37 /// </summary> 38 public static class RemarkExtension 39 { 40 public static string GetRemark(this Enum value) 41 { 42 Type type = value.GetType(); //typeof(Enum); 43 FieldInfo field = type.GetField(value.ToString());//反射获取字段信息 44 if (field.IsDefined(typeof(RemarkAttribute), true))//查看是否有该属性 45 { //获取字段上面的属性 46 RemarkAttribute remarkAttribute = (RemarkAttribute)field.GetCustomAttribute(typeof(RemarkAttribute), true); 47 return remarkAttribute.GetRemark(); 48 } 49 else 50 { 51 return value.ToString(); 52 } 53 } 54 }
扩展方法特特殊调用方式:UserState.Frozen.GetRemark()等价于RemarkExtension.GetRemark(UserState.Frozen)//原本调用方式静态类静态方法
给字段或是属性添加额外的行为 本示例是给object类型扩展一个方法来调用特性中的方法即添加额外行为
1 给实体类中添加特性行为 2 [Leng(2, 10)] 3 public string Password { set; get; } 4 5 public class LengAttribute : Attribute 6 { 7 private int _max; 8 private int _min; 9 10 public LengAttribute(int min, int max) 11 { 12 _max = max; 13 _min = min; 14 } 15 public bool Validate(object value) 16 { 17 if (value != null && value.ToString().Length > this._min && value.ToString().Length < this._max) 18 { 19 return true; 20 } 21 return false; 22 } 23 24 } 25 /// <summary> 26 /// object类型扩展方法 静态类+静态方法 然后参数前面加个this 27 /// </summary> 28 public static class ValidateExtension 29 { 30 public static bool Validate(this object ovalue) 31 { 32 Type type = ovalue.GetType(); //typeof(Enum); 33 foreach (var propertyInfo in type.GetProperties()) //反射获取字段信息 34 { 35 if (propertyInfo.IsDefined(typeof(LengAttribute), true))//查看是否有该属性 36 { //获取字段上面的属性 37 LengAttribute lengAttribute = (LengAttribute)propertyInfo.GetCustomAttribute(typeof(LengAttribute), true); 38 if (!lengAttribute.Validate(propertyInfo.GetValue(ovalue))) 39 { return false; } 40 } 41 } 42 return true; 43 } 44 }
调用方式
1 User user = new User(); 2 user.Name = "张三"; 3 user.Password = "123456"; 4 if (user.Validate()) //扩展方法的方式调用方法 5 { 6 Console.WriteLine($"{user.Name}验证通过"); 7 } 8 user.Password = "1"; 9 if (ValidateExtension.Validate(user))//普通方式调用静态方法静态类等价上述调用方式 10 { 11 Console.WriteLine($"{user.Name}的密码太简单不通过"); 12 }
目前.Net中特性在框架中运用的比较平凡,内部封装了很多特性,架构一个框架特性的运用少不了。