1. 什么是特性(Attribute)
1.1 概述
特性(Attribute) 就是在不破坏原有代码的情况下,在代码的元数据上附加一些信息的手段。
2. 特性的使用
2.1 特性的作用
-
attribute是反射的扩充,在结构体上声明一个attribute,本质是将该attribute附加在结构体的元数据上面,需要配合反射来进行访问。
-
如果要了解attribute,首先还是得理解什么是反射[C#反射详解]。
-
它允许我们在设计时添加attribute到类、成员等,然后可以在运行时通过反射进行检测(设计时决定运行时的行为),获取它们通过attribute标记的信息,从而根据这些信息做些不同的事情。
2.2 使用示例
特性的使用很简单,在结构声明的上一行,用"[ ]"扩起特性类名即可:
//在结构体上声明一个或多个attribute
//那么这个attribute就会作为一个静态的结构体附加到这个类上
//在程序运行时,框架会通过反射检查attribute标记,去实现相应的功能。
[Serializable]
[XmlRoot("MyClass")]
public class Sunshine
{
public int id;
...
//在一个元素(如类和属性,方法等等)上可以使用一个或多个特性进行修饰。
//特性可以拥有参数。
[NonSerialized]
public string name;
}
3. 预定义的特性
因为特性的灵活与方便,于是类库开发者,则提供了一系类预定义特性。
如[serializable] 标记可序列化的结构,[dllimport] 指示CLR 方法是从DLL导入等, 了解更多微软提供的特性可以查阅官方文档。
3.1 Obsolete特性 (简单示例)
当某些旧方法过时了,你不想要在使用这个方法了,可以用Obsolete特性来将程序结构标注为“过时”,并且在代码编译时会显示警告信息。下面是Obsolete特性的使用。
[Obsolete("类已经过时")]
public class MyClass
{
[Obsolete("方法已经过时,请使用NewSum()")]
public void Sum()
{
//注意: 使用Obsolete特性代码编译时只是显示了警告,但是代码仍然可以执行。
//而在使用两个参数的重载时,并将Obsolete的第二个参数设置为true时,将会标记为错误。
}
[Obsolete("Sum(int)", true)]
public void Sum(int i)
{
}
public void NewSum()
{
}
}
4. 自定义的特性
4.1 自定义一个特性
C#也允许我们自定义特性,特性其实就是一个类,直接或间接继承自Attribute
using System;
//自定义特性必须间接或直接继承Attribute类
//类的名字以Attribute后缀结构
public class CustomAttribute : Attribute
{
}
4.2 标记自定义特性
约定俗成用Attribute结尾,标记时就可以省略掉;可以用中括号“[ ]”包裹,然后标记到元素,其实就是调用构造函数;
using System;
namespace MyAttribute
{
[Custom]
public class Student
{
[Custom]
public int Id { get; set; }
public string Name { get; set; }
[Custom]
public void Study()
{
Console.WriteLine{this.Name});
}
}
}
4.3 调用自定义特性
就像在类中定义了属性,但是属性没有被调用,那么这个属性所蕴含的信息我们并不会知道。同理,仅仅声明了自定义特性,并且应用在某些程序结构上使程序结构和所应用的特性相关联,但是着仅仅相当于一个标识,我们并不没有检索这些特性蕴含的信息。
所以,我们就需要通过反射的方式来访问自定义特性。在上面我们定义并声明好了CustomAttribute特性标记,现在我们通过反射,去读取并且使用这个特性信息。
//反射获取这个类的信息
Type type = student.GetType();
//IsDefined(Type, Boolean) :检测某个特性是否应用到某个类上
//第一个参数,要接受检查的具体特性, 第二个布尔参数,是否搜索该类的继承链来查找这个特性
if(type.IsDefined(typeof(CustomAttribute), ture))
{
//GetCustomAttributes(Type, Boolean) :查找并且返回应用到该结构上特性的集合
//第一个参数,要接受检查的具体特性, 第二个布尔参数,是否搜索该类的继承链来查找这个特性
object[] oAttributeArray = type.GetCustomAttributes(typeof(CustomAttribute), true);
foreach(CustomAttribute attribute in oAttributeArray)
{
//调用特性信息或行为
attribute.Show();
}
//遍历检查属性是否有特性
foreach (var prop in type.GetProperties())
{
if (prop.IsDefined(typeof(CustomAttribute), true))
{
object[] oAttributeArrayProp = prop.GetCustomAttributes(typeof(CustomAttribute), true);
foreach (CustomAttribute attribute in oAttributeArrayProp)
{
attribute.Show();
}
}
}
//遍历检查方法是否有特性
foreach (var method in type.GetMethods())
{
if (method.IsDefined(typeof(CustomAttribute), true))
{
object[] oAttributeArrayMethod = method.GetCustomAttributes(typeof(CustomAttribute), true);
foreach (CustomAttribute attribute in oAttributeArrayMethod)
{
attribute.Show();
}
}
}
}
总结:我们可以通过反射的GetCustomAttributes方法来获取应用到结构上的特性的数组,来获取数据。无论是类,字段或是方法等等结构,我们都可以通过反射得到相应的结构引用,在通过结构的引用调用GetCustomAttributes方法来得到结构上的特性的数组,以此来触发特性。
注意:但是一般我们不会这样使用自定义特性,一般都是配合框架,通过继承框架中为我们写好的特性,进行定制操作。