03.特性Attribute

  • A+
所属分类:.NET技术
摘要

特性(Attribute)本质上是一个类,此类需要直接或间接继承 Attribute 类,特性为目标元素(比如类、方法、结构、枚举、组件等)提供关联附加信息,并在运行期以反射的方式来获取附加信息


1. 基本了解

1.1 简述说明

特性(Attribute)本质上是一个类,此类需要直接或间接继承 Attribute 类,特性为目标元素(比如类、方法、结构、枚举、组件等)提供关联附加信息,并在运行期以反射的方式来获取附加信息

说明:特性类的实例里没有验证逻辑,只有验证用到的规范数据(比如字符串长度)、提示信息等,而验证逻辑需要自己写

.Net 框架提供了两种类型的特性:预定义特性和自定义特性

1.2 特性应用

添加额外信息

可以使用特性附加需要的信息,例如:字段的中文名,实体字段对应数据表字段的名称

示例:Table 特性,指定表名,Route 指定路由路径

其它功能

例如,信息的效验,功能标识

示例:值的长度效验,类型效验等(Model验证就是很好的例子)

2. 特性限定

2.1 AttributeUsage 限定

AttributeUsageAttributeAttribute,用户指定特性使用限制,常用的有:AttributeTargetsAllowMultiple

2.2 AttributeTargets 目标限定

使用 AttributeTargets 表示指定Attribute限制用于哪类实体上,在这里,实体是指: classmethodconstructorfieldpropertyGenericParameter或者用All,表明可用于所有实体

每个target标记可以用|链接(组合),如AttributeTargets.Class|AttributeTargets.Method表示可用于class或者method上,以此为例

示例:无限定

[AttributeUsage(AttributeTargets.All)] public class AllTargetsAttribute : Attribute {} 

示例:限定只能标记在类上

[AttributeUsage(AttributeTargets.Class)] public class ClassTargetAttribute : Attribute {} 

示例:限定只能标记在方法上

[AttributeUsage(AttributeTargets.Method)] public class MethodTargetAttribute : Attribute {}  [AttributeUsage(AttributeTargets.Method,AllowMultiple = true)] public class MethodTargetAttribute : Attribute {} 

示例:限定只能标记在构造函数上

[AttributeUsage(AttributeTargets.Constructor)] public class ConstructorTargetAttribute : Attribute {} 

示例:限定只能标记在字段上

[AttributeUsage(AttributeTargets.Field)] public class FieldTargetAttribute : Attribute {} 

示例:限定只能标记在泛型类型参数上

[AttributeUsage(AttributeTargets.GenericParameter)] public class GenericParameterTargetAttribute : Attribute {} 

示例:限定标记在类与方法上

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class MethodAndClassTargetAttribute : Attribute {} 

2.2 AllowMultiple 重复限定

使用 AllowMultiple 表示是否可以多次标记在同一目标上,不指定默认 true

[AttributeUsage(AttributeTargets.Method,AllowMultiple = true)] public class CustomAttribute : Attribute {} 

3. 自定义特性

3.1 自定义步骤

  • 声明自定义特性,创建类
  • 构建自定义特性,写逻辑,功能
  • 在目标程序元素上应用自定义特性
  • 通过反射访问特性,调用特性逻辑,功能

3.2 定义特性

基本定义

一个新的自定义特性应派生(继承)自 System.Attribute

public class CustomAttribute : Attribute { 	... } 

带构造函数定义,类中默认有个无参构造方法

public class CustomAttribute : Attribute {     public CustomAttribute()     {         Console.WriteLine("调用子类无参构造函数");     }     public CustomAttribute(string text)     {         Console.WriteLine("调用子类有参构造函数:"+text);     } } 

带属性特性定义

public class CustomAttribute : Attribute {     public int index { get; set; } } 

带字段特性定义

public class CustomAttribute : Attribute {     public string name; } 

3.3 标记使用

标记在类上

[CustomAttribute] public class Studen {     ... } 

标记在方法上

public class Studen {     [CustomAttribute]     public void Show() { } } 

标记在属性上

public class Studen {     [CustomAttribute]     public int id { get; set; } } 

标记在字段上

public class Studen {     [CustomAttribute]     public int no; } 

标记在构造函数上

public class Studen {     [CustomAttribute]     public Studen()     {      } } 

标记在方法返回参数上

public class Studen {     [return:CustomAttribute]	// 多个特性逗号隔开     public void Show()     {      } } 

4. 综合示例

4.1 定义特性

定义验证特性,使用抽象(类)特性实现扩展,实现逻辑在Validate方法中

public abstract class AbstractValidateAttribute : Attribute {     public abstract bool Validate(object oValue); }  // 验证值长度 public class LengthAttribute : AbstractValidateAttribute {     public long Max { get; set; }     public long Min { get; set; }     public override bool Validate(object oValue)     {         return oValue != null && long.TryParse(oValue.ToString().Length.ToString(), out long lValue)              && lValue >= Min && lValue <= Max;     } }  // 验证非空 public class NullAttribute : AbstractValidateAttribute {     public override bool Validate(object oValue)     {         return oValue != null;     } } 

4.2 使用特性

public class Studen {     [NullAttribute]     [LengthAttribute(Max =10,Min =5)]     public string name { get; set; } } 

4.3 调用特性

缺陷:需要手动调用扩展方法,且扩展方法没有限制,功能单一(只能用于验证)

public static class AttributeExtend {     public static bool Validate<T>(this T t) where T : class     {         Type type = t.GetType();         foreach (var prop in type.GetProperties())         {              // 这里先判断,是为了提高性能             if (prop.IsDefined(typeof(AbstractValidateAttribute), true))             {                 object ovale = prop.GetValue(t);                 // 获取特性的实例,上面先判断之后,再获取实例                 foreach (AbstractValidateAttribute attribute                      in prop.GetCustomAttributes(typeof(AbstractValidateAttribute), true))                 {                     if (!attribute.Validate(ovale))                     {                         return false;                     }                 }             }         }         return true;     } } 

4.4 测试使用

static void Main(string[] args) {     Studen stu = new Studen     {         name = "起舞在人间"     };      Console.WriteLine(stu.Validate()); } 

5. 扩展补充

特性编译后内容

通过反编译工具得知,标记特性的元素,最终会在元素内部生成.custom的元素

.class public auto ansi beforefieldinit ConsoleApp2.Studen 	extends [mscorlib]System.Object { 	// Methods 	.method public hidebysig  		instance void Show () cil managed  	{ 		.custom instance void ConsoleApp2.CustomAttribute::.ctor() = ( 			01 00 00 00 		) 		// Method begins at RVA 0x2085 		// Code size 2 (0x2) 		.maxstack 8  		IL_0000: nop 		IL_0001: ret 	} // end of method Studen::Show  	.method public hidebysig specialname rtspecialname  		instance void .ctor () cil managed  	{ 		// Method begins at RVA 0x2088 		// Code size 8 (0x8) 		.maxstack 8  		IL_0000: ldarg.0 		IL_0001: call instance void [mscorlib]System.Object::.ctor() 		IL_0006: nop 		IL_0007: ret 	} // end of method Studen::.ctor  } // end of class ConsoleApp2.Studen 

特性,内部属性

在特性中声明属性,且此属性只能用于外部访问,内部赋值

public int text { get; private set; }