.NET 开发基金会/属性
.NET 开发基金会 | |
---|---|
此页面原始文本由 William "Scott" Baker 撰写
属性是一种“标记”代码元数据元素的方法,使用描述性信息,这些信息可以在运行时使用反射访问。属性必须直接或间接派生自System.Attribute。.NET 框架中存在大量属性;您也可以定义自己的属性。在代码中使用属性有三个方面
- 定义自定义属性类,包括以下步骤
- 将AttributeUsageAttribute属性分配给您的类。
- 编写代码来定义您的自定义属性类。
- 为您的类创建参数。
- 将属性分配给代码成员。
- 在运行时检索属性信息。
如前所述,.NET 框架中存在多个预定义属性;您可能已经在代码中使用过它们。特别是 XML 解析器在(反)序列化对象时严重依赖属性。您也可以定义自己的自定义属性,如下所示。定义自定义属性包括三个步骤
- 将AttributeUsageAttribute属性分配给您的类。
- 编写代码来定义您的自定义属性类。
- 为您的类创建参数。
注意:在 Visual Basic 中,必须在所有自定义属性上使用AttributeUsageAttribute属性。将AttributeUsageAttribute属性应用于类
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)] public Class QualityCheckAttribute : System.Attribute { // ... }
请注意使用“AttributeUsage”与“AttributeUsageAttribute”。按照惯例,所有属性都以“Attribute”后缀命名 - 但在代码中使用时可以省略后缀。这对于用户定义的属性也是如此;QualityCheckAttribute属性可以引用为
[QualityCheck] // or... [QualityCheckAttribute]
该AttributeUsageAttribute具有三个成员ValidOn、AllowMultiple和Inherited.
- 该ValidOn成员接受AttributeTargets枚举值,并将您的属性限制为您指定的代码类型。默认值为AttributeTargets.All。您可以将您的属性限制为类、枚举、返回值或以下列表中的任何内容
All (any element) Delegate GenericParameter Parameter Assembly Enum Interface Property Class Event Method ReturnValue Constructor Field Module* Struct *Module refers to a portable executable (.exe or .dll), and not a Visual Basic standard module.
您还可以将目标类型组合为按位 OR 运算以指定多个可接受值
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
- AllowMultiple是一个布尔值,它确定是否可以将属性多次应用于给定成员。默认值为false。以下示例说明了在代码元素上使用多个相同属性的实例
[QualityCheck("Scott Baker", "02/28/06", IsApproved = true, Comment = "This code follows all established guidelines. Release approved.")] [QualityCheck("Matt Kauffman", "01/15/06", IsApproved = false, Comment = "Code quality much improved. Minor revision required.")] [QualityCheck("Joe Schmoe", 01/01/06", IsApproved = false, Comment = "This code is a mess and needs a complete rewrite")] public class MyClass { // ... }
- 该Inherited成员确定是否将类上设置的属性继承到继承树中的更低级别类。默认值为 true
[AttributeUsage(AttributeTargets.Class)] public class AttrOneAttribute : Attribute { // ... } // This attribute will not be inherited [AttributeUsage(AttributeTargets.Class, Inherited = false)] public class AttrTwoAttribute : Attribute { // ... } [AttrOne] [AttrTwo] public class ClassOne { // ... } // This class inherits AttrOne from ClassOne, // but not AttrTwo public class ClassTwo : ClassOne { // ... }
- 属性是继承自System.Attribute的类,直接或间接继承
public Class QualityCheckAttribute : System.Attribute // direct { // ... } public Class FinalCheck : QualityCheckAttribute // indirect { // ... }
- 属性类具有AttributeUsageAttribute属性
[AttributeUsage(AllowMultiple = true, Inherited = false)] public Class QualityCheckAttribute : System.Attribute { // ... }
如前所述,在 VB 中必须使用AttributeUsageAttribute。在 C# 中,如果未声明,则会自动应用默认值。
public class QualityCheckAttribute : Attribute { public QualityCheckAttribute(string Name, string Date) // ... }
public class QualityCheckAttribute : Attribute { private string _name; private string _date; private bool isApproved; public bool IsApproved { get {return isApproved;} set {isApproved = value;} } public QualityCheckAttribute(string Name, string Date) { // ... } }
请记住,代码中的变量可以既是位置参数又是命名参数。如果我们要为 _name
和 _date
字段添加公共属性,我们可以将它们用作命名参数或位置参数。当然,这并不推荐:必需参数应为位置参数,可选参数应为命名参数。
您已经看到了将属性分配给代码成员的示例。但是,需要澄清一些要点。
- 消歧义是指在代码成员上使用属性的澄清。
- 语法 - 应用多个属性的方法不止一种。
public class MyAttribute : Attribute { [SomeAttribute("Hello")] public string MyMethod(aString) { return aString; } }
消除歧义可以解决这些问题。通过指定属性应用到的代码类型,我们可以消除混淆。以下代码显示属性应用于返回值值
public class MyAttribute : Attribute { [return : SomeAttribute] public string MyMethod(aString) { return aString; } }
下表列出了所有允许使用属性的声明;对于每个声明,第二列列出了声明中属性的可能目标。**粗体**的目标是默认目标。
Declaration Possible targets assembly assembly module module class type struct type interface type enum type delegate type, return method method, return parameter param field field property — indexer property property — get accessor method, return property — set accessor method, param, return event — field event, field, method event — property event, property event — add method, param event — remove method, param *Reference: Disambiguating Attribute Targets (C# Programming Guide)
人们可能会认为AttributeUsageAttribute 的 AttributeTargets在属性定义中将有助于防止这种混淆:这是错误的。编译器在解决冲突时不会使用AttributeUsageAttribute信息。即使您定义了一个属性使其仅应用于特定类型,例如AttributeTargets.Return,您仍然必须明确它应用于返回值类型,在应用属性时,否则编译器将使用默认目标方法类型,并抛出错误。
[AttrOne(...), AttrTwo(...)] // or... [AttrOne(...)] [AttrTwo(...)]
这两种方法是等效的。请记住,如果您要在单个大括号中指定多个属性,则它们必须应用于相同的目标类型。否则,您必须为每种类型提供单独的声明
[return : AttrOne(...), method : AttrTwo(...)] // <-- invalid! // instead, you must... [return : AttrOne(...)] [method : AttrTwo(...)]
能够声明和应用属性,除非我们能够检索这些数据并使用它们,否则并不是很有帮助。幸运的是,这是一个简单的过程。将解决三个基本场景
- 从成员中检索单个属性。
- 从成员中检索多个属性。
- 从多个成员中检索单个类型的属性。
要访问属性信息
- 声明属性类型的实例。
- 使用Attribute.GetCustomAttribute(类型, typeof)方法将属性读入实例。
- 使用实例的属性来读取值。
以下示例代码声明了一个类ExampleClass具有一个QualityCheck属性。该GetSingleAttribute方法接受目标成员类型和要查找的属性类型。该Attribute.GetCustomAttribute方法将属性信息检索到attr对象中,从中我们可以读取最重要的IsApproved属性
[QualityCheck("Scott Baker", "02/04/2006", IsApproved = false)] public class ExampleClass { public static void Main() { GetSingleAttribute(typeof(ExampleClass), typeof(QualityCheck)) } public static void GetSingleAttribute(Type targetType, Type attrType) { typeof(attrType) attr = (attrType)Attribute.GetCustomAttribute(targetType, typeof(attrType)); if (attr == null) { //... } else { Console.Writeline(attr.IsApproved); } }
要记住的一个重要因素是GetCustomAttribute方法设计为仅读取**一个**属性。GetCustomAttribute实际上检查是否有多个属性匹配 - 如果没有匹配,它将返回null,但如果有多个匹配,它将抛出AmbiguousMatchException。在检查属性时,唯一可以安全地使用GetCustomAttribute的是当属性的定义声明[AttributeUsage(AllowMultiple=false)].
读取成员上的多个属性实例与读取一个属性实例并没有太大区别;要读取多个属性,请使用复数GetCustomAttributes,它返回一个属性数组。然后,您可以遍历生成的数组并读取值
QualityCheck[] attrArray = (QualityCheck[])Attribute.GetCustomAttributes(t, typeof(QualityCheck)); foreach (QualityCheck attr in attrArray) { Console.Writeline(attr.IsApproved); }
如果要执行更复杂的操作,例如检查类中的每个方法是否具有 QualityCheck 属性,该怎么办?由于System.Reflection命名空间,我们甚至不需要费力。只需将所有成员(在本例中为方法)读入MemberInfo数组并遍历它们
using System.Reflection public class ExampleClass { public static void Main() { RetrieveAttributes(typeof(ExampleClass)); } public void RetrieveAttributes(Type t) { MemberInfo[] methodList = t.GetMethods(); foreach (MemberInfo m in methodList) { QualityCheck[] attrArray = (QualityCheck[])Attribute.GetCustomAttributes(m, typeof(QualityCheck)); foreach (QualityCheck attr in attrArray) { Console.Writeline(attr.IsApproved); } } } }