zoukankan      html  css  js  c++  java
  • 特性

    一、什么是特性

    特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。

    特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。

    特性的语法如下:

    [attribute(positional_parameters, name_parameter = value, ...)]
    element

     特性(Attribute)的名称和值是在方括号内规定的,放置在它所应用的元素之前。positional_parameters 规定必需的信息,name_parameter 规定可选的信息。

    二、预定义特性

    1、Obsolete特性

    这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。

    语法如下:

    [Obsolete(
       message
    )]
    [Obsolete(
       message,
       iserror
    )]

     其中:

    • 参数 message,是一个字符串,描述项目为什么过时的原因以及该替代使用什么。
    • 参数 iserror,是一个布尔值。如果该值为 true,编译器应把该项目的使用当作一个错误。默认值是 false(编译器生成一个警告)。

    请看下面的一个小例子:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace MyAttribute
     8 {
     9     [Obsolete("请不要使用该类了,该类已经过时了,请使用什么代替")]
    10     public class Student
    11     {
    12         public int Id { get; set; }
    13         public string Name { get; set; }
    14 
    15         public string Accont { get; set; }
    16 
    17         public long QQ { get; set; }
    18 
    19         public string Answer([Custom]string name) 
    20         {
    21             return $"This is {name}";
    22         }
    23     }
    24 }

    上面的例子中,在Student类上面使用了Obsolete特性来标注该类已经过时了。编译代码结果:

     三、自定义特性

    .Net 框架允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。该信息根据设计标准和应用程序需要,可与任何目标元素相关。

    创建并使用自定义特性包含四个步骤:

    • 声明自定义特性
    • 构建自定义特性
    • 在目标程序元素上应用自定义特性
    • 通过反射访问特性

    1、声明自定义特性

    在上面的例子中,使用F12查看Obsolete的定义:

    从上面的截图中可以看出,.NET框架中的预定义特性是继承自Attribute类,所以要自定义一个特性,只需要该类继承自Attribute即可,下面定义一个Custom自定义特性:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace MyAttribute
     8 {
     9     /// <summary>
    10     /// 自定义Custom特性
    11     /// </summary>
    12     public class CustomAttribute :Attribute
    13     {
    14       
    15     }
    16 }

     注意:所有的特性默认以Attribute结尾,但声明的时候可以不以Attribute结尾。

    2、构建自定义特性

    每个特性必须至少有一个构造函数。必需的定位( positional)参数应通过构造函数传递。下面的代码演示了CustomAttribute类:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace MyAttribute
     8 {
     9     /// <summary>
    10     /// 自定义Custom特性
    11     /// </summary>
    12     public class CustomAttribute :Attribute
    13     {
    14         /// <summary>
    15         /// 无参构造函数
    16         /// </summary>
    17          public CustomAttribute()
    18         {
    19 
    20         }
    21 
    22         /// <summary>
    23         /// 有参构造函数
    24         /// </summary>
    25         /// <param name="id"></param>
    26         public CustomAttribute(string description)
    27         {
    28             this.Description = description;
    29         }
    30 
    31         /// <summary>
    32         /// 属性
    33         /// </summary>
    34         public string Description { get; set; }
    35 
    36         /// <summary>
    37         /// 字段
    38         /// </summary>
    39         public string Remark = null;
    40 
    41         public void Show()
    42         {
    43             Console.WriteLine("This Is CustomAttribute");
    44         }
    45     }
    46 }

     3、在目标程序元素上应用自定义特性

    通过把特性放置在紧接着它的目标(类、方法、属性、字段等)上面,来应用该特性:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace MyAttribute
     8 {
     9     [Obsolete("请不要使用该类了,该类已经过时了")]
    10     [Custom("这是Custom自定义特性")]
    11     public class Student
    12     {
    13         public int Id { get; set; }
    14         public string Name { get; set; }
    15 
    16         public string Accont { get; set; }
    17 
    18         public long QQ { get; set; }
    19 
    20         public string Answer([Custom]string name) 
    21         {
    22             return $"This is {name}";
    23         }
    24     }
    25 }

     注意:

    1、如果在声明自定义特性的时候使用了Attribute结尾,那么应用自定义特性的时候可以把Attribute省略掉;如果声明的时候没有以Attribute结尾,那么应用自定义特性的时候就不能把Attribute省略掉。

    2、默认情况下相同的特性只能应用一次,如果想应用多次特性,那么需要给特性添加AttributeUsage特性,CustomAttribute特性修改如下:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace MyAttribute
     8 {
     9     /// <summary>
    10     /// 自定义Custom特性
    11     /// </summary>
    12     [AttributeUsage(AttributeTargets.All,AllowMultiple =true,Inherited =true)]
    13     public class CustomAttribute :Attribute
    14     {
    15         /// <summary>
    16         /// 无参构造函数
    17         /// </summary>
    18          public CustomAttribute()
    19         {
    20 
    21         }
    22 
    23         /// <summary>
    24         /// 有参构造函数
    25         /// </summary>
    26         /// <param name="id"></param>
    27         public CustomAttribute(string description)
    28         {
    29             this.Description = description;
    30         }
    31 
    32         /// <summary>
    33         /// 属性
    34         /// </summary>
    35         public string Description { get; set; }
    36 
    37         /// <summary>
    38         /// 字段
    39         /// </summary>
    40         public string Remark = null;
    41 
    42         public void Show()
    43         {
    44             Console.WriteLine("This Is CustomAttribute");
    45         }
    46     }
    47 }

     其中,AttributeTargets是枚举值,F12转到定义可以查看AttributeTargets的所有枚举值:

    AttributeTargets的枚举值表示Custom特性可以应用在哪些目标上面。例如:AttributeTargets的枚举值是Class,则表示CustomAttribute只能应用在类上面。这里枚举值是All,表示可以在任何类型上面使用该特性。默认情况下枚举值是All。

    AllowMultiple表示该特性是否可以在类型上面多次使用:

    这里AllowMultiple的值为true,表示可以在类型上面多次使用该特性。如果为false,则表示只能使用一次。默认情况下是false。

    Inherited表示该特性是否可以由子类继承:

    默认情况下Inherited为true。

    这是在看Student类:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace MyAttribute
     8 {
     9     [Obsolete("请不要使用该类了,该类已经过时了")]
    10     [Custom("这是Custom自定义特性")]//使用有参构造
    11     [Custom()]//使用无参构造
    12     public class Student
    13     {
    14         public int Id { get; set; }
    15 
    16         /// <summary>
    17         /// 在属性上面使用Custom特性
    18         /// </summary>
    19         [Custom("这是Name属性")]
    20         public string Name { get; set; }
    21 
    22         public string Accont { get; set; }
    23 
    24         public long QQ { get; set; }
    25 
    26         /// <summary>
    27         /// 在方法和参数上面使用Custom特性
    28         /// </summary>
    29         /// <param name="name"></param>
    30         /// <returns></returns>
    31         [Custom("这是Answer方法")]
    32         public string Answer([Custom("这是方法参数")]string name) 
    33         {
    34             return $"This is {name}";
    35         }
    36     }
    37 }

     注意:如果一个类型上面多次使用了同一种特性,那么特性可以写在一起,中间用逗号隔开,例如上面的定义和下面的是同样的效果:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace MyAttribute
     8 {
     9     [Obsolete("请不要使用该类了,该类已经过时了")]
    10     [Custom("这是Custom自定义特性"),Custom,Custom(),Custom(Remark ="备注")]
    11 
    12     public class Student
    13     {
    14         public int Id { get; set; }
    15 
    16         /// <summary>
    17         /// 在属性上面使用Custom特性
    18         /// </summary>
    19         [Custom("这是Name属性")]
    20         public string Name { get; set; }
    21 
    22         public string Accont { get; set; }
    23 
    24         public long QQ { get; set; }
    25 
    26         /// <summary>
    27         /// 在方法、方法参数、方法的返回值上面使用Custom特性
    28         /// </summary>
    29         /// <param name="name"></param>
    30         /// <returns></returns>
    31         [Custom("这是Answer方法")]//方法上面应用特性
    32         [return:Custom()] //方法的返回值应用特性
    33         public string Answer([Custom("这是方法参数")]string name) 
    34         {
    35             return $"This is {name}";
    36         }
    37     }
    38 }

     注意:在Web API中FromBaby和FromUri就是给方法的参数应用特性。

    4、通过反射访问特性

    定义一个Manager类来管理特性:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Reflection;
     5 using System.Text;
     6 using System.Threading.Tasks;
     7 
     8 namespace MyAttribute
     9 {
    10     /// <summary>
    11     /// 管理特性
    12     /// </summary>
    13     public class Manager
    14     {
    15         public static void Show(Student student)
    16         {
    17             // 获取类型
    18             Type type = typeof(Student); //或者使用student.GetType();
    19             // 找到类型上面的特性    type.IsDefined表示找类型上面的特性
    20             if (type.IsDefined(typeof(CustomAttribute), true))//检查有没有  性能高
    21             {
    22                 //GetCustomAttribute 获取特性 type.GetCustomAttribute表示找到类型上面定义的特性,表示调用构造函数创建一个CustomAttribute类型的对象
    23                 CustomAttribute attribute = (CustomAttribute)type.GetCustomAttribute(typeof(CustomAttribute), true);
    24                 // attribute.Description表示特性类里面的属性  attribute.Remark表示特性类里面的字段
    25                 Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
    26                 attribute.Show();
    27             }
    28 
    29 
    30             #region 获取ID属性上面定义的特性
    31             // 获取Id属性
    32             PropertyInfo property = type.GetProperty("Id");
    33             //检查Id属性上面是否定义了CustomAttribute特性
    34             if (property.IsDefined(typeof(CustomAttribute), true))
    35             {
    36                 CustomAttribute attribute = (CustomAttribute)property.GetCustomAttribute(typeof(CustomAttribute), true);
    37                 Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
    38                 attribute.Show();
    39             }
    40             #endregion
    41 
    42             #region 获取Answer()方法上面定义的特性
    43             // 获取Answer方法
    44             MethodInfo method = type.GetMethod("Answer");
    45             if (method.IsDefined(typeof(CustomAttribute), true))
    46             {
    47                 CustomAttribute attribute = (CustomAttribute)method.GetCustomAttribute(typeof(CustomAttribute), true);
    48                 Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
    49                 attribute.Show();
    50             }
    51             #endregion
    52 
    53             #region 获取参数定义的特性
    54             ParameterInfo parameter = method.GetParameters()[0];
    55             if (parameter.IsDefined(typeof(CustomAttribute), true))
    56             {
    57                 CustomAttribute attribute = (CustomAttribute)parameter.GetCustomAttribute(typeof(CustomAttribute), true);
    58                 Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
    59                 attribute.Show();
    60             }
    61             #endregion
    62 
    63             #region 获取返回值定义的特性
    64             ParameterInfo returnParameter = method.ReturnParameter;
    65             if (returnParameter.IsDefined(typeof(CustomAttribute), true))
    66             {
    67                 CustomAttribute attribute = (CustomAttribute)returnParameter.GetCustomAttribute(typeof(CustomAttribute), true);
    68                 Console.WriteLine($"{attribute.Description}_{attribute.Remark}");
    69                 attribute.Show();
    70             }
    71             #endregion
    72             
    73             string result = student.Answer("Tom");
    74             Console.WriteLine(result);
    75         }
    76     }
    77 }

    Main()方法里面调用:

     1 using MyAttribute.Extension;
     2 using System;
     3 using System.Collections.Generic;
     4 using System.Linq;
     5 using System.Text;
     6 using System.Threading.Tasks;
     7 
     8 namespace MyAttribute
     9 {
    10     class Program
    11     {
    12         static void Main(string[] args)
    13         {
    14             Student student = new Student();
    15             student.Id = 123;
    16             student.Name = "time";
    17             // 使用Manager类管理Student
    18             Manager.Show(student);
    19 
    20             Console.ReadKey();
    21         }
    22     }
    23 }

     结果:

    四、应用特性

    场景一:用户状态的枚举值,定义的是英文的字段,需要输出中文含义。枚举定义如下:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace MyAttribute.Extension
     8 {
     9     /// <summary>
    10     /// 枚举类型 用户状态
    11     /// </summary>
    12     public enum UserState
    13     {
    14         /// <summary>
    15         /// 正常
    16         /// </summary>
    17         Normal = 0,
    18         /// <summary>
    19         /// 冻结
    20         /// </summary>
    21         Frozen = 1,
    22 
    23         /// <summary>
    24         /// 删除
    25         /// </summary>
    26         Deleted = 2
    27     }
    28 }

     普通做法:根据枚举值进行判断,然后输出中文含义:

     1 UserState userState = UserState.Normal;
     2 switch(userState)
     3 {
     4         case UserState.Normal:
     5                  Console.WriteLine("正常");
     6                  break;
     7         case UserState.Frozen:
     8                  Console.WriteLine("冻结");
     9                  break;
    10         case UserState.Deleted:
    11                  Console.WriteLine("删除");
    12                  break;
    13 }

     这种写法违反开不原则,不利于以后的扩展,下面使用特性实现。

    先定义Remark特性:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 using System.Reflection;
     7 
     8 namespace MyAttribute.Extension
     9 {
    10     /// <summary>
    11     /// RemarkAttribute 特性
    12     /// </summary>
    13     public class RemarkAttribute  :Attribute
    14     {
    15         private string _Remark = null;
    16         /// <summary>
    17         /// 有参构造
    18         /// </summary>
    19         /// <param name="remark"></param>
    20         public RemarkAttribute(string remark)
    21         {
    22             this._Remark = remark;
    23         }
    24 
    25         public string GetRemark()
    26         {
    27             return _Remark;
    28         }
    29     }
    30 }

     UserState枚举修改如下:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace MyAttribute.Extension
     8 {
     9     /// <summary>
    10     /// 枚举类型 用户状态
    11     /// </summary>
    12     public enum UserState
    13     {
    14         /// <summary>
    15         /// 正常
    16         /// </summary>
    17         [Remark("正常")]
    18         Normal = 0,
    19         /// <summary>
    20         /// 冻结
    21         /// </summary>
    22         [Remark("冻结")]
    23         Frozen = 1,
    24 
    25         /// <summary>
    26         /// 删除
    27         /// </summary>
    28         [Remark("删除")]
    29         Deleted = 2
    30     }
    31 }

     对Enum类型进行扩展:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Reflection;
     5 using System.Text;
     6 using System.Threading.Tasks;
     7 
     8 namespace MyAttribute.Extension
     9 {
    10     public static class EnumExtension
    11     {
    12         /// <summary>
    13         /// Enum的扩展方法,静态类、静态方法 第一个参数前面添加this关键字
    14         /// </summary>
    15         /// <param name="value"></param>
    16         /// <returns></returns>
    17         public static string GetRemark(this Enum value)
    18         {
    19             // 获取类型
    20             Type type = value.GetType();
    21             // 获取字段
    22             FieldInfo field = type.GetField(value.ToString());
    23             // 判断字段上面是否定义了RemarkAttribute特性
    24             if (field.IsDefined(typeof(RemarkAttribute)))
    25             {
    26                 // 创建实例
    27                 RemarkAttribute attribute = (RemarkAttribute)field.GetCustomAttribute(typeof(RemarkAttribute));
    28                 // 返回RemarkAttribute特性里面的GetRemark()方法
    29                 return attribute.GetRemark();
    30             }
    31             else
    32             {
    33                 return value.ToString();
    34             }
    35         }
    36     }
    37 }

     Main()方法里面调用:

     1 using MyAttribute.Extension;
     2 using System;
     3 using System.Collections.Generic;
     4 using System.Linq;
     5 using System.Text;
     6 using System.Threading.Tasks;
     7 
     8 namespace MyAttribute
     9 {
    10     class Program
    11     {
    12         static void Main(string[] args)
    13         {
    14             Student student = new Student();
    15             student.Id = 123;
    16             student.Name = "time";
    17             // 使用Manager类管理Student
    18             //Manager.Show(student);
    19 
    20 
    21             UserState userState = UserState.Normal;
    22             //switch(userState)
    23             //{
    24             //    case UserState.Normal:
    25             //        Console.WriteLine("正常");
    26             //        break;
    27             //    case UserState.Frozen:
    28             //        Console.WriteLine("冻结");
    29             //        break;
    30             //    case UserState.Deleted:
    31             //        Console.WriteLine("删除");
    32             //        break;
    33             //}
    34             Console.WriteLine(userState.GetRemark());
    35             Console.ReadKey();
    36         }
    37     }
    38 }

     结果:

    二、做数据校验

    Student中有QQ这个属性,范围是10000-999999999999,校验QQ属性的值在这个范围区间内。

    1、定义一个RangeAttribute特性,用来验证属性范围

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace MyAttribute.Extension
     8 {
     9     /// <summary>
    10     /// 定义LongAttribute特性,并且特性只能应用在字段和属性上面
    11     /// </summary>
    12     [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property)]
    13     public class RangeAttribute :Attribute
    14     {
    15         /// <summary>
    16         /// 最小范围
    17         /// </summary>
    18         private long _MinRange = 0;
    19 
    20         /// <summary>
    21         /// 最大范围
    22         /// </summary>
    23         private long _MaxRange = 0;
    24 
    25         public RangeAttribute(long min,long max)
    26         {
    27             this._MinRange = min;
    28             this._MaxRange = max;
    29         }
    30 
    31         /// <summary>
    32         /// 检查属性范围
    33         /// </summary>
    34         /// <param name="value"></param>
    35         /// <returns></returns>
    36         public bool Check(object value)
    37         {
    38             if(value!=null && !string.IsNullOrWhiteSpace(value.ToString()))
    39             {
    40                 if(long.TryParse(value.ToString(),out long IResult))
    41                 {
    42                     if(IResult>this._MinRange && IResult<this._MaxRange)
    43                     {
    44                         return true;
    45                     }
    46                 }
    47             }
    48 
    49             return false;
    50         }
    51     }
    52 }

     2、在Student类的QQ属性上面应用特性

    1 [Range(10001,999999999999)]
    2 public long QQ { get; set; }

     3、对Object类型进行扩展

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Reflection;
     5 using System.Text;
     6 using System.Threading.Tasks;
     7 
     8 namespace MyAttribute.Extension
     9 {
    10     /// <summary>
    11     /// object类型的验证扩展
    12     /// </summary>
    13     public static class ObjectExtension
    14     {
    15         /// <summary>
    16         /// 对object类型扩展一个Validate的方法
    17         /// </summary>
    18         /// <param name="obj"></param>
    19         /// <param name="msg">输出参数,输出验证信息。如果验证通过,输出空字符串;如果验证不通过,输出具体信息</param>
    20         /// <returns></returns>
    21         public static bool Validate(this object obj,out string msg)
    22         {
    23             // 获取类型
    24             Type type = obj.GetType();
    25             // 获取属性
    26             PropertyInfo[] propertyInfos= type.GetProperties();
    27             foreach(PropertyInfo prop in propertyInfos)
    28             {
    29                 if(prop.IsDefined(typeof(LongAttribute)))
    30                 {
    31                     LongAttribute attribute = (LongAttribute)prop.GetCustomAttribute(typeof(LongAttribute));
    32                     if(!attribute.Check(prop.GetValue(obj)))
    33                     {
    34                         msg = prop.Name + "检查失败";
    35                         return false;
    36                     }
    37                 }
    38             }
    39             msg = "";
    40             return true;
    41 
    42         }
    43     }
    44 }

     4、在Manager类里面使用Validate扩展方法

    // 验证
    string msg = string.Empty;
    bool tfResult=  student.Validate(out msg);
    if(!tfResult)
    {
          Console.WriteLine(msg);
    }

    5、在Main()方法里面调用

    1 Student student = new Student();
    2 student.Id = 123;
    3 student.Name = "time";
    4 student.QQ = 9999;
    5 // 使用Manager类管理Student
    6 Manager.Show(student);

     结果:

    如果这时候Student里面增加了Name属性,并且要验证Name属性的长度,这时需要增加一个验证属性长度的特性:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace MyAttribute.Extension
     8 {
     9     /// <summary>
    10     ///  验证长度的特性,只能应用于字段和属性上面
    11     /// </summary>
    12     [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property)]
    13     public class LengthAttribute:Attribute
    14     {
    15         /// <summary>
    16         /// 最小长度
    17         /// </summary>
    18         private int _MinLength = 0;
    19 
    20         /// <summary>
    21         /// 最大长度
    22         /// </summary>
    23         private int _MaxLength = 0;
    24 
    25         public LengthAttribute(int min, int max)
    26         {
    27             this._MinLength = min;
    28             this._MaxLength = max;
    29         }
    30 
    31         /// <summary>
    32         /// 检查属性长度
    33         /// </summary>
    34         /// <param name="value"></param>
    35         /// <returns></returns>
    36         public bool Check(object value)
    37         {
    38             if (value != null && !string.IsNullOrWhiteSpace(value.ToString()))
    39             {
    40                 if (long.TryParse(value.ToString(), out long IResult))
    41                 {
    42                     if (IResult > this._MinLength && IResult < this._MaxLength)
    43                     {
    44                         return true;
    45                     }
    46                 }
    47             }
    48 
    49             return false;
    50         }
    51     }
    52 }

     在Student类的Name属性上面应用LengthAttribute特性:

    1 [Length(5,10)]
    2 public string Name { get; set; }

     在ObjectExtension里面增加长度的验证:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Reflection;
     5 using System.Text;
     6 using System.Threading.Tasks;
     7 
     8 namespace MyAttribute.Extension
     9 {
    10     /// <summary>
    11     /// object类型的验证扩展
    12     /// </summary>
    13     public static class ObjectExtension
    14     {
    15         /// <summary>
    16         /// 对object类型扩展一个Validate的方法
    17         /// </summary>
    18         /// <param name="obj"></param>
    19         /// <param name="msg">输出参数,输出验证信息。如果验证通过,输出空字符串;如果验证不通过,输出具体信息</param>
    20         /// <returns></returns>
    21         public static bool Validate(this object obj,out string msg)
    22         {
    23             // 获取类型
    24             Type type = obj.GetType();
    25             // 获取属性
    26             PropertyInfo[] propertyInfos= type.GetProperties();
    27             foreach(PropertyInfo prop in propertyInfos)
    28             {
    29                 // 检查属性上面是否定义了RangeAttribute特性
    30                 if (prop.IsDefined(typeof(RangeAttribute)))
    31                 {
    32                     RangeAttribute attribute = (RangeAttribute)prop.GetCustomAttribute(typeof(RangeAttribute));
    33                     if(!attribute.Check(prop.GetValue(obj)))
    34                     {
    35                         msg = string.Format($"属性{ prop.Name}范围检查失败");
    36                         return false;
    37                     }
    38                 }
    39 
    40                 // 检查属性上面是否定义了LengthAttribute特性
    41                 if (prop.IsDefined(typeof(LengthAttribute)))
    42                 {
    43                     LengthAttribute attribute = (LengthAttribute)prop.GetCustomAttribute(typeof(LengthAttribute));
    44                     if (!attribute.Check(prop.GetValue(obj)))
    45                     {
    46                         msg = string.Format($"属性{ prop.Name}长度检查失败");
    47                         return false;
    48                     }
    49                 }
    50             }
    51             msg = "";
    52             return true;
    53 
    54         }
    55     }
    56 }

     最后在Main()方法里面调用:

    1 Student student = new Student();
    2 student.Id = 123;
    3 student.Name = "time";
    4 // 使用Manager类管理Student
    5 Manager.Show(student);

     结果:

    仔细查看ObjectExtension扩展类:每增加一个特性,扩展方法里面就要增加一段相同的代码(只是特性的类型不同),那么能不能做到增加特性,而这里不需要修改呢?请看下面的修改:

    1、定义一个抽象类继承自Attribute,里面有一个抽象的Check()方法,定义如下:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace MyAttribute.Extension
     8 {
     9     /// <summary>
    10     /// 抽象基类,继承自Attribute
    11     /// </summary>
    12     public abstract class AbstractValidateAttribute:Attribute
    13     {
    14         public abstract bool Check(object value);
    15     }
    16 }

    2、修改RangeAttribute和LengthAttribute两个特性类,都继承自AbstractValidateAttribute基类

     RangeAttribute类:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace MyAttribute.Extension
     8 {
     9     /// <summary>
    10     /// 定义LongAttribute特性,并且特性只能应用在字段和属性上面
    11     /// </summary>
    12     [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property)]
    13     public class RangeAttribute : AbstractValidateAttribute
    14     {
    15         /// <summary>
    16         /// 最小范围
    17         /// </summary>
    18         private long _MinRange = 0;
    19 
    20         /// <summary>
    21         /// 最大范围
    22         /// </summary>
    23         private long _MaxRange = 0;
    24 
    25         public RangeAttribute(long min,long max)
    26         {
    27             this._MinRange = min;
    28             this._MaxRange = max;
    29         }
    30 
    31         /// <summary>
    32         /// 重写基类方法 检查属性范围
    33         /// </summary>
    34         /// <param name="value"></param>
    35         /// <returns></returns>
    36         public override bool Check(object value)
    37         {
    38             if(value!=null && !string.IsNullOrWhiteSpace(value.ToString()))
    39             {
    40                 if(long.TryParse(value.ToString(),out long IResult))
    41                 {
    42                     if(IResult>this._MinRange && IResult<this._MaxRange)
    43                     {
    44                         return true;
    45                     }
    46                 }
    47             }
    48 
    49             return false;
    50         }
    51     }
    52 }

     LengthAttribute类:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Threading.Tasks;
     6 
     7 namespace MyAttribute.Extension
     8 {
     9     /// <summary>
    10     ///  验证长度的特性,只能应用于字段和属性上面
    11     /// </summary>
    12     [AttributeUsage(AttributeTargets.Field|AttributeTargets.Property)]
    13     public class LengthAttribute: AbstractValidateAttribute
    14     {
    15         /// <summary>
    16         /// 最小长度
    17         /// </summary>
    18         private int _MinLength = 0;
    19 
    20         /// <summary>
    21         /// 最大长度
    22         /// </summary>
    23         private int _MaxLength = 0;
    24 
    25         public LengthAttribute(int min, int max)
    26         {
    27             this._MinLength = min;
    28             this._MaxLength = max;
    29         }
    30 
    31         /// <summary>
    32         /// 重写基类方法 检查属性长度
    33         /// </summary>
    34         /// <param name="value"></param>
    35         /// <returns></returns>
    36         public override bool Check(object value)
    37         {
    38             if (value != null && !string.IsNullOrWhiteSpace(value.ToString()))
    39             {
    40                 if (long.TryParse(value.ToString(), out long IResult))
    41                 {
    42                     if (IResult > this._MinLength && IResult < this._MaxLength)
    43                     {
    44                         return true;
    45                     }
    46                 }
    47             }
    48             return false;
    49         }
    50     }
    51 }

     3、修改ObjectExtension扩展类

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Reflection;
     5 using System.Text;
     6 using System.Threading.Tasks;
     7 
     8 namespace MyAttribute.Extension
     9 {
    10     /// <summary>
    11     /// object类型的验证扩展
    12     /// </summary>
    13     public static class ObjectExtension
    14     {
    15         /// <summary>
    16         /// 对object类型扩展一个Validate的方法
    17         /// </summary>
    18         /// <param name="obj"></param>
    19         /// <param name="msg">输出参数,输出验证信息。如果验证通过,输出空字符串;如果验证不通过,输出具体信息</param>
    20         /// <returns></returns>
    21         public static bool Validate(this object obj,out string msg)
    22         {
    23             // 获取类型
    24             Type type = obj.GetType();
    25             // 获取属性
    26             PropertyInfo[] propertyInfos= type.GetProperties();
    27             foreach(PropertyInfo prop in propertyInfos)
    28             {
    29                 // 判断属性上面是否定义了AbstractValidateAttribute特性
    30                 if (prop.IsDefined(typeof(AbstractValidateAttribute),true))
    31                 {
    32                     // 属性上面可能会定义多个特性,所以这里使用数组
    33                     object[] attributeArray = prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true);
    34                     foreach(AbstractValidateAttribute attribute in attributeArray)
    35                     {
    36                         if (!attribute.Check(prop.GetValue(obj)))
    37                         {
    38                             msg = string.Format($"属性{ prop.Name}检查失败");
    39                             return false;
    40                         }
    41                     }
    42                 }
    43 
    44                 //// 检查属性上面是否定义了RangeAttribute特性
    45                 //if (prop.IsDefined(typeof(RangeAttribute)))
    46                 //{
    47                 //    RangeAttribute attribute = (RangeAttribute)prop.GetCustomAttribute(typeof(RangeAttribute));
    48                 //    if(!attribute.Check(prop.GetValue(obj)))
    49                 //    {
    50                 //        msg = string.Format($"属性{ prop.Name}范围检查失败");
    51                 //        return false;
    52                 //    }
    53                 //}
    54 
    55                 //// 检查属性上面是否定义了LengthAttribute特性
    56                 //if (prop.IsDefined(typeof(LengthAttribute)))
    57                 //{
    58                 //    LengthAttribute attribute = (LengthAttribute)prop.GetCustomAttribute(typeof(LengthAttribute));
    59                 //    if (!attribute.Check(prop.GetValue(obj)))
    60                 //    {
    61                 //        msg = string.Format($"属性{ prop.Name}长度检查失败");
    62                 //        return false;
    63                 //    }
    64                 //}
    65             }
    66             msg = "";
    67             return true;
    68 
    69         }
    70     }
    71 }

     4、运行结果:

    经过上面的修改以后,如果以后要新增一个特性,那么该特性只需要在本类中重写基类的Check()方法即可,而不需要在修改ObjectExtension扩展类。

  • 相关阅读:
    软件原则
    Optional
    雪花算法原理
    AOP
    trycatchfinally
    lambda表达式
    BeanUtils属性转换工具
    @Transactional 失效
    用户线程&&守护线程
    卡顿问题
  • 原文地址:https://www.cnblogs.com/dotnet261010/p/9111954.html
Copyright © 2011-2022 走看看