本教程展示如何创建自定义属性类,如何在代码中 使用它们,以及如何通过反射查询它们。
教程
属性提供功能强大的方法以将声明信息与 C# 代码 (类型、方法、属性等)相关联。与程序实体关联后,属性可在运行 时查询,并可以以任意多种方式使用。
属性的用法 示例包括:
将帮助文档与程序实体关联 (通过 Help 属性)。
将值编辑器关联到 GUI 框架中的特定类型(通过 ValueEditor 属 性)。
除一个完整的示例外,本教程还包 括以下主题:
声明属性类
第一 件需要做的事情是声明属性。
使用属性 类
创建属性后,随即将属性与特定的程序元素关联。
通过反射访问属性
属性与程序元素关 联后,即可使用反射查询属性存在及其值。
声明属性类
在 C# 中声明属性很简 单:它采取从 System.Attribute 继承的类声明 的形式,并已用 AttributeUsage 属性标记,如下 所示:
using System;
[A ttributeUsage(AttributeTargets .All)]
public class HelpAtt ribute : System.Attribute
{
public readonly string Url;
public string Topic // Topic i s a named parameter
{
get
{
return topic;
}
set
{
topic = value;
}
}
pu blic HelpAttribute(string url) // url is a positional param eter
{
this.Url = url;
}
priv ate string topic;
}
代码讨论
属性 AttributeU sage 指定该属性可以应用于的语言元素。
属性类是从 System.Attribute 派生的公共类, 至少有一个公共构造函数。
属性类有两种类型的 参数:
“定位参数”,每次使用属性时都必须指 定这些参数。定位参数被指定为属性类的构造函数参数。在上面的示 例中,url 便是一个定位参数。
“命名参数 ”,可选。如果使用属性时指定了命名参数,则必须使用参数的名称 。通过包含非静态字段或属性来定义命名参数。在上面的示例中,T opic 便是一个命名参数。
属性参数限制为下列类型的常数值:
简单类型 (bool、byte、char、short、int、long 、float 和 double)
strin g
System.Type
enums
对象(对象类型的属性参数的参数必 须是属于上述类型之一的常数值。)
以上任意类 型的一维数组
Attr ibuteUsage 属性的参数
属性 Att ributeUsage 提供声明属性的基础机制。
AttributeUsage 具有一个定位参数:
AllowOn 指定可以将属性赋给的程序元 素(类、方法、属性、参数等)。该参数的有效值可以在 .NET Framework 中的 System.Attribute s.AttributeTargets 枚举中找到。该参数的默 认值是所有程序元素 (AttributeElements.A ll)。
AttributeUsage 有一个命名参数:
AllowMul tiple,一个布尔值,指示是否可以为一个程序元素指定多个属 性。该参数的默认值为 false。
使 用属性类
以下是使用上一节中声明的属性的简单示 例:
[HelpAttribute("http ://localhost/MyClassInfo")]
class MyClass
{
}
< br>本示例中,HelpAttribute 属性与 MyCl ass 关联。
注意 根据约定,所 有属性名称都以单词“Attribute”结束,以便将它们与 .NET Framework 中的其他项区分。但是,在代码中 使用属性时不需要指定属性后缀。例如,可以如下指定 HelpA ttribute:
[Help("ht tp://localhost/MyClassInfo")] // [Help] == [HelpAttribute]
class MyClass
{
}
通过 反射访问属性
属性与程序元素关联后,可以使用反 射查询属性存在及其值。查询属性的主要反射方法包含在 Syst em.Reflection.MemberInfo 类(Get CustomAttributes 方法族)中。下面的示例演示 使用反射获取对属性的访问的基本方法:
clas s MainClass
{
public static void Main()
{ System.Reflection.Memb erInfo info = typeof(MyClass);
object[] attributes = info.GetCustomAttributes(tru e);
for (int i = 0;
i <
attributes.Length;
i ++)
{
System .Console.WriteLine(attributes[ i]);
}
}
}
示例
下面是集合所有部分的完整 示例。
// AttributesTutori al.cs
// This example shows the use of class and method a ttributes.
using System ;
using System.Reflection;
< br>using System.Collections;
// The IsTested class is a user-defined custom attribu te class.
// It can be appl ied to any declaration includi ng
// - types (struct, cla ss, enum, delegate)
// - m embers (methods, fields, event s, properties, indexers)
// It is used with no arguments.
public class IsTestedAttri bute : Attribute
{
p ublic override string ToString ()
{
return "Is Tested";
}
}
// The AuthorAttribute cla ss is a user-defined attribute class.
// It can be applie d to classes and struct declar ations only.
// It takes on e unnamed string argument (the author's name).
// It has one optional named argument Ve rsion, which is of type int.[AttributeUsage(AttributeTar gets.Class | AttributeTargets. Struct)]
public class Autho rAttribute : Attribute
{
// This constructor speci fies the unnamed arguments to the attribute class.
pu blic AuthorAttribute(string na me)
{
this.n ame = name;
this.ve rsion = 0;
}
// This property is readonly (it has no set accessor)
// so it cannot be used as a named argument to this attrib ute.
public string Name
{
get
{
retur n name;
}
}< br>
// This property is read-write (it has a set acce ssor)
// so it can be u sed as a named argument when u sing this
// class as a n attribute class.
publ ic int Version
{
get
{
return version;
}
set
{
version = value;
}
}< br>
public override str ing ToString()
{
string value = "Author : " + Name;
if (versi on != 0)
{
value += " Version : " + Version.ToString();
}
return value;
< br> }
private st ring name;
private int version;
}
// Here y ou attach the AuthorAttribute user-defined custom attribute to
// the Account class. T he unnamed string argument is passed to the
// AuthorAtt ribute class's constructor whe n creating the attributes.
[Author("Joe Programmer")]
class Account
{
// A ttach the IsTestedAttribute cu stom attribute to this method.
[IsTested]
publ ic void AddOrder(Order orderTo Add)
{
order s.Add(orderToAdd);
}
private ArrayList ord ers = new ArrayList();
}
// Attach the AuthorAttri bute and IsTestedAttribute cus tom attributes
// to this class.
// Note the use of t he 'Version' named argument to the AuthorAttribute.
[Auth or("Jane Programmer", Version = 2), IsTested()]
class Ord er
{
// add stuff he re ...
}
class MainC lass
{
private static bool IsMemberTested(MemberInf o member)
{
f oreach (object attribute in me mber.GetCustomAttributes(true) )
{
if (attribute is IsTestedAttri bute)
{
return true;
}
}
return false;
}
private static void Dump Attributes(MemberInfo member)< br> {
Console.Wr iteLine("Attributes for : " + member.Name);
forea ch (object attribute in member .GetCustomAttributes(true))
{
Cons ole.WriteLine(attribute);
}
}
p ublic static void Main()
{
// display attr ibutes for Account class
DumpAttributes(typeof(Ac count));
// dis play list of tested members
foreach (MethodInfo m ethod in (typeof(Account)).Get Methods())
{
if (IsMemberTested(me thod))
{
Console.WriteLine( "Member {0} is tested!", metho d.Name);
}
else
{
Console.W riteLine("Member {0} is NOT te sted!", method.Name);
}
}
Console.WriteLine();
// display attributes for Order class
Du mpAttributes(typeof(Order));
// display attri butes for methods on the Order class
foreach (Met hodInfo method in (typeof(Orde r)).GetMethods())
{
if (IsMemberTes ted(method))
{ Console.Write Line("Member {0} is tested!", method.Name);
}< br> else
{
Console .WriteLine("Member {0} is NOT tested!", method.Name);
}
}
Console.WriteLine();
}
}
输出
Attr ibutes for : Account
Author : Joe Programmer
Member Ge tHashCode is NOT tested!
Me mber Equals is NOT tested!
Member ToString is NOT tested!
Member AddOrder is tested!
Member GetType is NOT test ed!
Attributes for : Or der
Author : Jane Programme r Version : 2
Is Tested
Member GetHashCode is NOT test ed!
Member Equals is NOT te sted!
Member ToString is NO T tested!
Member GetType is NOT tested!