特性,特性限定约束,自定义特性,其它


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; }


到达胜利之前无法回头!