属性
特性提供了一种强大的元数据或声明性信息与代码(程序集、类型、方法、属性等)相关联的方法。 将特性与程序实体相关联后,可以在运行时使用反射这项技术查询特性。
属性具有以下属性:
- 属性将元数据添加到程序。 元数据 是有关程序中定义的类型的信息。 所有 .NET 程序集都包含一组指定的元数据,用于描述程序集中定义的类型和类型成员。 可以添加自定义属性来指定所需的任何其他信息。
- 可以将一个或多个属性应用于整个程序集、模块或较小的程序元素,例如类和属性。
- 特性可以接受与方法和属性相同的参数。
- 程序可以使用反射检查其自己的元数据或其他程序中的元数据。
反射提供描述程序集、模块和类型的对象(Type 类型)。 可以使用反射动态创建类型的实例、将类型绑定到现有对象或从现有对象获取类型,并调用其方法或访问其字段和属性。 如果在代码中使用属性,反射使你能够访问它们。 有关详细信息,请参阅 属性。
以下是一个使用 GetType() 方法的简单反射示例,该方法是所有从 Object
基类继承的类型所共有的,用于获取变量的类型:
注意
请确保在 .cs 文件的顶部添加 using System;
和 using System.Reflection;
。
// Using GetType to obtain type information:
int i = 42;
Type type = i.GetType();
Console.WriteLine(type);
输出为:System.Int32
。
以下示例使用反射来获取已加载程序集的全名。
// Using Reflection to get information of an Assembly:
Assembly info = typeof(int).Assembly;
Console.WriteLine(info);
输出类似于:System.Private.CoreLib, Version=7.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e
。
注意
C# 关键字 protected
和 internal
在中间语言(IL)中没有意义,在反射 API 中不使用。 IL 中的相应术语是 系列 和 程序集。 要使用反射来识别方法 internal
,请使用属性 IsAssembly。 若要标识 protected internal
方法,请使用 IsFamilyOrAssembly。
使用属性
属性几乎可以放置在任何声明中,但特定属性可能会限制其有效声明的类型。 在 C# 中,通过将括在方括号([]
)中的属性的名称放在应用实体的声明上方来指定特性。
在此示例中,SerializableAttribute 属性用于将特定特征应用于类:
[Serializable]
public class SampleClass
{
// Objects of this type can be serialized.
}
具有 DllImportAttribute 属性的方法声明如下例所示:
[System.Runtime.InteropServices.DllImport("user32.dll")]
extern static void SampleMethod();
可以将多个属性放置在声明上,如以下示例所示:
void MethodA([In][Out] ref double x) { }
void MethodB([Out][In] ref double x) { }
void MethodC([In, Out] ref double x) { }
可以为给定实体多次指定某些属性。 此类多用属性的示例 ConditionalAttribute:
[Conditional("DEBUG"), Conditional("TEST1")]
void TraceMethod()
{
// ...
}
注意
按照约定,所有属性名称以“Attribute”一词结尾,以将它们与 .NET 库中的其他项区分开来。 但是,在代码中使用属性时,无需指定属性后缀。 例如,[DllImport]
等效于 [DllImportAttribute]
,但 DllImportAttribute
是 .NET 类库中属性的实际名称。
属性参数
许多属性具有参数,这些参数可以是有位置的、未命名的或命名的。 必须按特定顺序指定任何位置参数,并且不能省略。 命名参数是可选的,可以按任意顺序指定。 首先指定位置参数。 例如,这三个属性等效:
[DllImport("user32.dll")]
[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
[DllImport("user32.dll", ExactSpelling=false, SetLastError=false)]
第一个参数(DLL 名称)是位置参数,始终在最前面;其他参数是命名参数。 在这种情况下,两个命名参数默认为 false,因此可以省略它们。 位置参数对应于属性构造函数的参数。 命名或可选参数对应于特性的属性或字段。 有关默认参数值的信息,请参阅各个属性的文档。
有关允许的参数类型的详细信息,请参阅 C# 语言规范属性 部分。
特性目标
属性的 目标 是属性适用的实体。 例如,特性可以应用于类、特定方法或整个程序集。 默认情况下,属性应用于其后面的元素。 但是,还可以显式标识属性是应用于方法,还是应用于其参数,还是应用于其返回值。
若要显式标识属性目标,请使用以下语法:
[target : attribute-list]
下表显示了可能的 target
值列表。
目标值 | 适用于 |
---|---|
assembly |
整个程序集 |
module |
当前程序集模块 |
field |
类或结构中的字段 |
event |
活动 |
method |
方法或 get 和 set 属性访问器 |
param |
方法参数或 set 属性访问器参数 |
property |
财产 |
return |
方法、属性索引器或 get 属性访问器的返回值 |
type |
结构、类、接口、枚举或委托 |
可以指定 field
目标值,以将属性应用于为 自动实现的属性创建的后盾字段。
以下示例演示如何将属性应用于程序集和模块。 有关详细信息,请参阅 通用属性 (C#)。
using System;
using System.Reflection;
[assembly: AssemblyTitleAttribute("Production assembly 4")]
[module: CLSCompliant(true)]
以下示例演示如何将属性应用于 C# 中的方法、方法参数和方法返回值。
// default: applies to method
[ValidatedContract]
int Method1() { return 0; }
// applies to method
[method: ValidatedContract]
int Method2() { return 0; }
// applies to parameter
int Method3([ValidatedContract] string contract) { return 0; }
// applies to return value
[return: ValidatedContract]
int Method4() { return 0; }
注意
无论 ValidatedContract
被定义为在哪些目标上有效,仍然必须指定 return
目标,即便 ValidatedContract
被定义为仅适用于返回值也是如此。 换句话说,编译器不会使用 AttributeUsage
信息来解析不明确的属性目标。 有关详细信息,请参阅 AttributeUsage。
特性的常见用途
以下列表包括代码中属性的一些常见用途:
- 使用 Web 服务中的
WebMethod
属性标记方法,以指示该方法应该可通过 SOAP 协议调用。 有关详细信息,请参阅 WebMethodAttribute。 - 描述在与本机代码互操作时如何处理方法参数。 有关详细信息,请参阅 MarshalAsAttribute。
- 描述类、方法和接口的 COM 属性。
- 使用 DllImportAttribute 类调用非托管代码。
- 从标题、版本、说明或商标方面描述程序集。
- 描述要序列化并暂留类的哪些成员。
- 描述如何在类成员和 XML 节点之间进行映射,以便进行 XML 序列化。
- 描述方法的安全要求。
- 指定用于强制实施安全性的特征。
- 通过实时 (JIT) 编译器控制优化,使代码更易于调试。
- 获取方法调用方的相关信息。
反射概述
在以下情况下,反射非常有用:
- 当必须访问程序元数据中的属性时。 有关详细信息,请参阅 检索存储在属性中的信息。
- 检查和实例化程序集中的类型。
- 用于在运行时生成新类型。 使用 System.Reflection.Emit 中的类。
- 执行后期绑定,访问在运行时创建的类型上的方法。 请参阅文章 "动态加载和使用类型"。
相关部分
更多信息,请参阅: