您的位置:首页 > 编程语言 > C#

.NET特性的一些基本概念 via C#

2007-05-16 20:19 381 查看
如果您使用过 C++,或许对包含关键字(如 public 和 private)的声明比较熟悉,这些关键字提供有关类成员的其他信息。另外,这些关键字通过描述类成员对其他类的可访问性来进一步定义类成员的行为。由于编译器被显式设计为识别预定义关键字,因此传统上您没有机会创建自己的关键字。但是,公共语言运行库允许您添加类似关键字的描述性声明(称为属性 (Attribute))来批注编程元素,如类型、字段、方法和属性 (Property)。

为运行库编译代码时,该代码被转换为 Microsoft 中间语言 (MSIL),并同编译器生成的元数据一起被放到可移植可执行 (PE) 文件的内部。属性 (Attribute) 使您得以向元数据中放置额外的描述性信息,并可使用运行库反射服务提取该信息。当您声明从 System.Attribute 派生的特殊类的实例时,编译器创建属性 (Attribute)。

.NET Framework 出于多种原因使用属性 (Attribute) 并通过它们解决若干问题。属性 (Attribute) 描述如何将数据序列化,指定用于强制安全性的特性,并限制实时 (JIT) 编译器的优化,从而使代码易于调试。属性 (Attribute) 还可以记录文件名或代码作者,或在窗体开发阶段控制控件和成员的可见性。

可使用属性 (Attribute) 以几乎所有可能的方式描述代码,并以富有创造性的新方式影响运行库行为。使用属性 (Attribute),可以向 C#、C++ 托管扩展、Microsoft Visual Basic 2005 或其他任何以运行库为目标的语言添加自己的描述性元素,而不必重新编写编译器。

通过下列过程将属性应用到代码元素。

1 通过从 .NET Framework 导入属性的命名空间来定义新的属性或使用现有属性。

2 初始化紧挨在要描述的元素前面的属性,并用所需标志或信息调用该属性的构造函数。

属性在您编译代码时被发送到元数据中,并可通过运行库反射服务用于公共语言运行库以及任何自定义工具或应用程序。

按照约定,所有属性名都以 Attribute 结尾。但是,某些以运行库为目标的语言(如 Visual Basic 和 C#)不要求指定属性的全名。例如,如果要初始化 System.ObsoleteAttribute,只需将其引用为 Obsolete 即可。

下面的代码示例显示如何声明 System.ObsoleteAttribute,该属性将代码标记为过时。字符串 "Will be removed in next version" 被传递到该属性。当调用该属性所描述的代码时,该属性将产生编译器警告以显示所传递的字符串。
using System;
public class MainApp
{
public static void Main()
{
//This generates a compile-time warning.
int MyInt = Add(2,2);
}
//Specify attributes between square brackets in C#.
//This attribute is applied only to the Add method.
[Obsolete("Will be removed in next version")]
public static int Add( int a, int b)
{
return (a+b);
}
}

如果要在程序集级别应用属性,请使用 Assembly 关键字。下列代码显示在程序集级别应用的 AssemblyNameAttribute。

using System.Reflection;
[assembly:AssemblyName("MyAssembly")]

应用该属性时,字符串 "MyAssembly" 被放到文件元数据部分的程序集清单中。可以使用 MSIL 反汇编程序 (Ildasm.exe) 或通过创建检索该属性的自定义程序来查看该属性。

要设计自己的自定义属性,不必掌握许多新的概念。如果熟悉面向对象的编程,并且知道如何设计类,就已具备了所需的大部分知识。自定义属性本质上是直接或间接地从 System.Attribute 派生的传统类。与传统类一样,自定义属性也包含存储和检索数据的方法。

正确设计自定义属性类的主要步骤如下:

应用 AttributeUsageAttribute

声明属性类

声明构造函数

声明属性

本节描述上述每个步骤,并以自定义属性示例结束。

应用 AttributeUsageAttribute
自定义属性声明以 AttributeUsageAttribute 开始,而该属性定义属性类的一些主要属性。例如,可指定属性是否可被其他类继承,或指定属性可应用到哪些元素。下列代码段说明了如何使用 AttributeUsageAttribute。

[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]

System.AttributeUsageAttribute 类包含三个对自定义属性的创建具有重要意义的成员:AttributeTargets、Inherited 和 AllowMultiple。

AttributeTargets 成员
在前面的示例中指定了 AttributeTargets.All,指示该属性可以应用到所有程序元素。还可以指定 AttributeTargets.Class,指示属性只可以应用于某个类;或指定 AttributeTargets.Method,指示属性只可以应用于某个方法。所有程序元素都可通过这种方式由自定义属性标记,以便对其进行描述。

还可传递 AttributeTargets 的多个实例。下列代码段指定自定义属性可应用到任何类或方法。

[AttributeUsage (AttributeTargets.Class | AttributeTargets.Method)]

继承属性
Inherited 属性指示属性是否可由从该属性应用到的类派生的类继承。该属性采用 true(默认值)或 false 标志。例如,在下面的代码示例中,MyAttribute 的默认 Inherited 值为 true,而 YourAttribute 的 Inherited 值为 false。

//This defaults to Inherited = true.
public class MyAttribute :Attribute
{
}

[AttributeUsage( Inherited = false)]
public class YourAttribute : Attribute
{
}

然后,这两个属性应用到基类 MyClass 中的某个方法。

public class MyClass
{
[MyAttribute]
[YourAttribute]
public virtual void MyMethod()
{
//...
}
}

最后,YourClass 类从基类 MyClass 继承。方法 MyMethod 显示 MyAttribute,而不是 YourAttribute。

public class YourClass: MyClass
{
//MyMethod will have MyAttribute but not YourAttribute.
public override void MyMethod()
{
//...
}

}

AllowMultiple 属性
AllowMultiple 属性指示元素中是否可存在属性的多个实例。如果设置为 true,则允许存在多个实例;如果设置为 false(默认值),则只允许存在一个实例。

在下面的代码示例中,MyAttribute 的默认 AllowMultiple 值为 false,而 YourAttribute 的对应值为 true。

//This defaults to AllowMultiple = false.
public class MyAttribute :Attribute
{
}

[AttributeUsage(AllowMultiple = true)]
public class YourAttribute : Attribute
{
}

当应用这些属性的多个实例时,MyAttribute 将生成编译器错误。下面的代码示例显示 YourAttribute 的有效用法和 MyAttribute 的无效用法。

public class MyClass
{
//This produces an error.
//Duplicates are not allowed.
[MyAttribute]
[MyAttribute]
public void MyMethod() {
//...
}

//This is valid.
[YourAttribute]
[YourAttribute]
public void YourMethod(){
//...
}
}

如果 AllowMultiple 属性和 Inherited 属性都设置为 true,则从另一个类继承的类可以继承一个属性,并在同一子类中应用同一属性的另一个实例。如果 AllowMultiple 设置为 false,则父类中的所有属性值将被子类中同一属性的新实例重写。

声明属性类
应用了 AttributeUsageAttribute 后,可以开始定义属性细节。属性类的声明与传统类的声明类似,如下列代码所示:

public class MyAttribute : System.Attribute
{
// . . .
}

此属性定义说明了下列几点:

属性类必须声明为公共类。

按照约定,属性类的名称以单词 Attribute 结尾。虽然并不要求这样,但出于可读性目的,建议采用此约定。应用属性时,可以选择是否包含 Attribute 一词。

所有属性类都必须直接或间接地从 System.Attribute 继承。

在 Microsoft Visual Basic 中,所有自定义属性类都必须具有 AttributeUsageAttribute 属性。

声明构造函数
用构造函数初始化属性的方法与对待传统类的方法相同。下列代码段阐释了典型的属性构造函数。该公共构造函数采用一个参数,并将其值设置为与成员变量相等。

public MyAttribute(bool myvalue)
{
this.myvalue = myvalue;
}

可重载该构造函数以适应值的不同组合。如果同时为自定义属性类定义了属性,则在初始化属性时可使用命名参数和定位参数的组合。通常情况下,将所有必选参数定义为定位参数,将所有可选参数定义为命名参数。在这种情况下,如果没有必选参数,则无法初始化属性。其他所有参数都是可选参数。请注意,在 Visual Basic 中,属性类的构造函数不应使用 ParamArray 参数。

下面的代码示例显示如何使用可选参数和必选参数应用使用上例中的构造函数的属性。它假定该属性有一个必选布尔值和一个可选字符串属性。

//One required (positional) and one optional (named) parameter are applied.
[MyAttribute(false, OptionalParameter = "optional data")]
//One required (positional) parameter is applied.
[MyAttribute(false)]

声明属性
如果需要定义命名参数,或者要提供一种容易的方法来返回由属性存储的值,请声明属性。应将属性属性声明为带有要返回的数据类型说明的公共实体。定义将保存属性值的变量,并将该变量与 get 方法和 set 方法关联。下面的代码示例说明如何在属性中实现一个简单的属性。

public bool MyProperty
{
get {return this.myvalue;}
set {this.myvalue = value;}
}

自定义属性示例
本节具体表现前面的信息,并显示如何设计一个简单的属性来记录有关代码段作者的信息。该示例中的属性存储程序员的名字和级别,和关于该代码是否已被复查过的信息。该示例使用三个私有变量存储要保存的实际值。每个变量用获取和设置这些值的公共属性表示。最后,用两个必选参数定义构造函数。

[AttributeUsage(AttributeTargets.All)]
public class DeveloperAttribute : System.Attribute
{

//Private fields.
private string name;
private string level;
private bool reviewed;

//This constructor defines two required parameters: name and level.

public DeveloperAttribute(string name,string level)
{
this.name = name;
this.level = level;
this.reviewed = false;
}

//Define Name property.
//This is a read-only attribute.

public virtual string Name
{
get {return name;}
}

//Define Level property.
//This is a read-only attribute.

public virtual string Level
{
get {return level;}
}

//Define Reviewed property.
//This is a read/write attribute.

public virtual bool Reviewed
{
get {return reviewed;}
set {reviewed = value;}
}
}

可通过下列方法之一,使用全名 DeveloperAttribute 或使用缩写名 Developer 来应用该属性。

C# 复制代码
[Developer("Joan Smith", "1")]
[Developer("Joan Smith", "1", Reviewed = true)]

第一个示例显示只用必选命名参数应用的属性,而第二个示例显示同时使用必选参数和可选参数应用的属性。

检索存储在属性中的信息

检索自定义属性的过程很简单。首先,声明要检索的属性实例。然后,使用 Attribute.GetCustomAttribute 方法将新属性初始化为要检索的属性值。初始化新属性后,只需使用其属性获取值即可。

本节描述下列检索属性的方法:

检索属性的一个实例

检索应用到同一范围的属性的多个实例

检索应用到不同范围的属性的多个实例

检索属性的一个实例
在下面的示例中,DeveloperAttribute(详见前一节的介绍)应用到类级别上的 MainApp 类。GetAttribute 方法使用 GetCustomAttribute 检索存储在类级别上 DeveloperAttribute 中的值,然后将这些值显示到控制台。

using System;

[Developer("Joan Smith", "42", Reviewed = true)]
class MainApp
{
public static void Main()
{
//Call function to get and display the attribute.
GetAttribute(typeof(MainApp));
}

public static void GetAttribute(Type t)
{

//Get instance of the attribute.
DeveloperAttribute MyAttribute = (DeveloperAttribute) Attribute.GetCustomAttribute(t, typeof (DeveloperAttribute));

if(null == MyAttribute)
{
Console.WriteLine("The attribute was not found.");
}
else
{
//Get the Name value.
Console.WriteLine("The Name Attribute is: {0}." , MyAttribute.Name);
//Get the Level value.
Console.WriteLine("The Level Attribute is: {0}." , MyAttribute.Level);
//Get the Reviewed value.
Console.WriteLine("The Reviewed Attribute is: {0}." , MyAttribute.Reviewed);
}
}
}

该程序在执行时显示下列文本。
The Name Attribute is: Joan Smith.
The Level Attribute is: 42.
The Reviewed Attribute is: True.

如果未找到该属性,则 GetCustomAttribute 方法将 MyAttribute 初始化为一个空值。该示例检查 MyAttribute 以查找这样的实例,如未找到任何属性,则通知用户。如果在类范围中未找到 DeveloperAttribute,则下列消息将显示到控制台。

The attribute was not found.

该示例假定属性定义位于当前命名空间中。如果属性定义不在当前命名空间中,请务必导入该属性定义所驻留的命名空间。

检索应用到同一范围的属性的多个实例
在前一个示例中,要检查的类和要查找的特定属性被传递到 GetCustomAttribute。如果类级别上只应用属性的一个实例,则该代码将运行良好。但是,如果在同一类级别上应用属性的多个实例,则 GetCustomAttribute 方法不检索所有信息。如果同一个属性的多个实例应用到相同的范围,可使用 Attribute.GetCustomAttributes 将属性的所有实例放到一个数组中。例如,如果在同一个类的类级别上应用 DeveloperAttribute 的两个实例,则可修改 GetAttribute 方法以显示同时存在于这两个属性中的信息。请记住,要在同一级别上应用多个属性,必须在定义该属性时在 AttributeUsageAttribute 中将 AllowMultiple 属性设置为 true。

下面的代码示例说明如何使用 GetCustomAttributes 方法创建在任意给定类中引用 DeveloperAttribute 的所有实例的数组。然后,所有属性的值将显示到控制台。

public static void GetAttribute(Type t)
{
DeveloperAttribute[] MyAttribute =
(DeveloperAttribute[]) Attribute.GetCustomAttributes(t, typeof (DeveloperAttribute));

if(null == MyAttribute)
{
Console.WriteLine("The attribute was not found.");
}
else
{
for(int i = 0 ; i < MyAttribute.Length ; i++)
{
//Get the Name value.
Console.WriteLine("The Name Attribute is: {0}." , MyAttribute[i].Name);
//Get the Level value.
Console.WriteLine("The Level Attribute is: {0}." , MyAttribute[i].Level);
//Get the Reviewed value.
Console.WriteLine("The Reviewed Attribute is: {0}.", MyAttribute[i].Reviewed);
}
}
}

如果没有找到任何属性,该代码将提醒用户。否则,将显示两个 DeveloperAttribute 实例中都包含的信息。

检索应用到不同范围的属性的多个实例
GetCustomAttributes 方法和 GetCustomAttribute 方法不搜索整个类和返回该类中某个属性的所有实例。相反,它们一次只搜索一个指定方法或成员。如果将具有同一属性的某个类应用到每个成员,并要检索应用到这些成员的所有属性值,则必须向 GetCustomAttributes 和 GetCustomAttribute 分别提供每个方法或成员。

下面的代码示例将类用作参数,并在类级别上以及该类的每个方法上搜索 DeveloperAttribute(先前已定义)。

public static void GetAttribute(Type t)
{

DeveloperAttribute att;

//Get the class-level attributes.

//Put the instance of the attribute on the class level in the att object.
att = (DeveloperAttribute) Attribute.GetCustomAttribute (t, typeof (DeveloperAttribute));

if(null == att)
{
Console.WriteLine("No attribute in class {0}.\n", t.ToString());
}
else
{
Console.WriteLine("The Name Attribute on the class level is: {0}.", att.Name);
Console.WriteLine("The Level Attribute on the class level is: {0}.", att.Level);
Console.WriteLine("The Reviewed Attribute on the class level is: {0}.\n", att.Reviewed);
}

//Get the method-level attributes.

//Get all methods in this class, and put them
//in an array of System.Reflection.MemberInfo objects.
MemberInfo[] MyMemberInfo = t.GetMethods();

//Loop through all methods in this class that are in the
//MyMemberInfo array.
for(int i = 0; i < MyMemberInfo.Length; i++)
{
att = (DeveloperAttribute) Attribute.GetCustomAttribute(MyMemberInfo[i], typeof (DeveloperAttribute));
if(null == att)
{
Console.WriteLine("No attribute in member function {0}.\n" , MyMemberInfo[i].ToString());
}
else
{
Console.WriteLine("The Name Attribute for the {0} member is: {1}.", MyMemberInfo[i].ToString(), att.Name);
Console.WriteLine("The Level Attribute for the {0} member is: {1}.", MyMemberInfo[i].ToString(), att.Level);
Console.WriteLine("The Reviewed Attribute for the {0} member is: {1}.\n", MyMemberInfo[i].ToString(), att.Reviewed);
}
}
}

如果在方法级别或类级别上未找到 DeveloperAttribute 的任何实例,GetAttribute 方法将通知用户未找到任何属性,并显示不包含该属性的方法或类的名称。如果找到了属性,则控制台将显示 Name、Level 和 Reviewed 字段。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: