特性
特性是一种将自定义信息添加到代码元素(程序集、类型、成员、返回值、参数和泛型类型参数)的扩展机制
特性在没有破坏类型封装的前提下,可以加点额外的信息和行为
定义特性
特性是一个继承Attribute类的类,在其中可以定义属性、字段、方法、构造函数等,与正常的类并无两样,但是通常我们会以Attribute作为类名的结尾
获取特性实例
特性可以作为普通类来使用,但是这样就没有了意义,完全可以使用普通类
特性的正确用法是通过[ ]对类中信息进行标注,以便对其进行扩展
在实例化时通过反射查找类时同时查找特性,对其进行验证操作
使用特性
// 抽象特性,为了后面可以查找多种验证
public abstract class AbstractValidateAttribute:Attribute
{
public abstract bool Validate(object value);
}
// 定义特性
[AttributeUsage(AttributeTargets.Property|AttributeTargets.Field)]
public class NotNullAttribute:AbstractValidateAttribute
{
public override bool Validate(object value)
{
return value != null ? true : false;
}
}
// 使用特性
public static bool Validate<T>(this T tModel) where T : BaseModel
{
Type type = tModel.GetType();
foreach (var propertyInfo in type.GetProperties())
{
if (propertyInfo.IsDefined(typeof(AbstractValidateAttribute),true))
{
object[] attributeArray = propertyInfo.GetCustomAttributes(typeof(AbstractValidateAttribute), true);
foreach (AbstractValidateAttribute attribute in attributeArray)
{
if (!attribute.Validate(propertyInfo.GetValue(tModel)))
{
return false;
}
}
}
}
return true;
}
前面说特性不结合反射则基本是没有用的,这里的例子就是结合反射的一个具体例子
首先定义了一个抽象基类,这是为了后面查找时可以查找到多个继承自该抽象类的验证特性
接着定义了一个具体的NotNull特性,这个特性定义了一个验证方法Validate,如果值为null则返回false,不为null则返回true
然后定义了一个名为Validate的扩展方法,注意这个方法和上面的方法不是同一个方法,该方法接收一个泛型对象,然后通过反射获取该对象的所有属性
并判断每一个属性是否包含AbstractValidateAttribute特性的定义,也就是属性上方是否使用了[<特性>],如果有,则获取特性实例数组
接着再通过特性的实例对象来调用前面特性定义的方法,并根据其执行结果参与判断,做出最终的判断
在其他地方,原本直接进行处理的代码,可以添加验证代码,根据特性的结果来决定如何处理
结果论
在我使用特性时,最大的迷惑是如何获取到特性,以及如何将其关联起来
[NotNull]
public string Name { get; set; }
当我们在一个类模型中定义了这样一个特性,开始的时候始终不明白
- 怎样知道是哪个属性绑定了特性?
- 通过反射获取到所有的属性,然后通过IsDefined方法判断是否存在指定的特性
- 怎样使用特性?
- 如果IsDefined判断确实定义了指定的特性,则通过 属性.GetCustomAttributes方法获取特性实例,然后调用实例方法,如果需要属性的值,可以通过反射的GetValue来获取
- 特性什么时候生效?
- 很遗憾附加特性并没有什么用,特性只是添加附加信息,当然也可以附加动作,但是它并不能自动执行,只能在需要的时候手动去选择执行,比如上面的定义了NotNull特性,然后为了方便还定义了Validate扩展方法,这个特性主要是用在model上的,所以如果属性定义了NotNull特性,在给model赋值时一样可以赋值null,但是我们可以在后面用到model时,选择调用Validate扩展方法,这样就可以验证这么model的值是否符合要求,符合要求继续操作,否则可以考虑抛出异常
- 特性的重点是两个类之间提供了一种关联性,即普通类通过在某个位置使用[<特性>]来描述关联,通过反射获取到这种关联来在合适的时候执行特性类的内容,并可能会得到一个结果,这个结果作为下一步操作的依据