默认情况下特性是应用于跟随其后的对象的,因此在许多时候,在使用特性时都会省略表示特性目标的关键字。以下是特性应用于目标对象时的完整格式。
[<目标>:<特性列表>]
特性目标关键字及相关说明:
- assembly —— 表示特性将应用于当前程序集,通常放在程序集中命名空间或所有类型定义之前。
- module —— 用于当前模块,该特性用得比较少。
- field —— 该特性用于字段,如果特性后紧跟着字段的声明代码,则该关键字可以省略。
- event —— 特性用于事件,默认情况下也可以省略。
- method —— 该特性用于方法,也可以用于属性中的get和set访问器,该特性用得较少。
- param —— 表示特性用于方法中的参数或属性定义中的set访问器中的参数(value),默认情况下该关键字也可以省略。
- property —— 指示特性用于属性,默认情况下也可以省略。
- type —— 表示特性用于类型,如类、结构、委托、枚举等。默认情况下也可以省略。
- return —— 特性用于方法的返回值,或者属性中get访问器的返回值。由于无法在返回值前面附加特性声明,所以,若要为返回值应用特性,retrun关键字不能省略。
例如,若要为方法的返回值应用特性,应如下:
[return:MarshalAs(UnmanagedType.SysInt)]
public int Compute()
{
return 0;
}
通过反射技术检索特性
特性可以理解为附加在类型上的一些扩展信息,因此可以通过在类型中找到指定的特性来验证代码的调用方是否符合特定的要求。
下面是一个简单的示例,定义一个TypeInfoAttribute特性,它有一个Description属性,表示类型的描述信息。接着通过反射技术来获取到这些描述信息。
TypeInfoAttribute特性类如下:
[AttributeUsage(AttributeTargets.All)]
public class TypeInfoAttribute:Attribute
{
public string Description{get;set;}
}
AttributeTargets.All表示该特性可以应用于所有目标。接下来,声明一个枚举和一个类,并应用TypeInfoAttribute特性。
[TypeInfo(Description = "这是我们定义的枚举类型。")]
enum TestEnum
{
One = 1,
Two,
Three
}
[TypeInfo(Description = "这是我们定义的一个类。")]
public class Goods{}
然后,在Main方法中把以上定义的两个类型的TypeInfoAttribute特性读出来。代码如下:
static void Main(string[] args)
{
//用Type类的GetCustomAttributes方法可以获取指定类型上附加的特性列表
//返回一个object类型的数组,数组中的每个元素表示一个特性类的实例
//GetCustomAttributes方法的其中一个重载可以将一个Type作为参数传递
//该Type表示要获取的特性的类型,typeof运算符返回某个类型的一个Type
//本例中我们要获取TypeInfoAttribute特性列表
//由于上面定义TestEnum枚举和Goods类时,只应用了一个TypeInfoAtribute特性
//因此获取到的特性实例数组的元素个数总为1
object[] attrs = typeof(TestEnum).GetCustomAttributes(typeof(TypeInfoAttribute),false);
if(attrs.Length > 0)
{
TypeInfoAttribute ti = attrs[0] as TypeInfoAttribute;
System.Console.WriteLine($"TestEnum枚举的描述信息:{ti.Description}");
}
attrs = typeof(Goods).GetCustomAttributes(typeof(TypeInfoAttribute),false);
if(attrs.Length >0)
{
TypeInfoAttribute ti = attrs[0] as TypeInfoAttribute;
System.Console.WriteLine($"Goods类的描述信息:{ti.Description}");
}
}
运行示例后,输出如下:
TestEnum枚举的描述信息:这是我们定义的枚举类型。
Goods类的描述信息:这是我们定义的一个类。