- A+
1. 基本了解
1.1 简述说明
特性(Attribute
)本质上是一个类,此类需要直接或间接继承 Attribute
类,特性为目标元素(比如类、方法、结构、枚举、组件等)提供关联附加信息,并在运行期以反射的方式来获取附加信息
说明:特性类的实例里没有验证逻辑,只有验证用到的规范数据(比如字符串长度)、提示信息等,而验证逻辑需要自己写
.Net
框架提供了两种类型的特性:预定义特性和自定义特性
1.2 特性应用
添加额外信息
可以使用特性附加需要的信息,例如:字段的中文名,实体字段对应数据表字段的名称
示例:Table
特性,指定表名,Route
指定路由路径
其它功能
例如,信息的效验,功能标识
示例:值的长度效验,类型效验等(Model
验证就是很好的例子)
2. 特性限定
2.1 AttributeUsage 限定
AttributeUsage
是 Attribute
的 Attribute
,用户指定特性使用限制,常用的有:AttributeTargets
,AllowMultiple
2.2 AttributeTargets 目标限定
使用 AttributeTargets
表示指定Attribute
限制用于哪类实体上,在这里,实体是指: class
、method
、constructor
、field
、property
、GenericParameter
或者用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; }