特性 (C#)

使用特性,可以有效地将元数据或声明性信息与代码(程序集、类型、方法、属性等)相关联。 将特性与程序实体相关联后,可以在运行时使用反射这项技术查询特性。 有关详细信息,请参阅反射 (C#)

特性具有以下属性:

  • 特性向程序添加元数据。 元数据是程序中定义的类型的相关信息。 所有 .NET 程序集都包含一组指定的元数据,用于描述程序集中定义的类型和类型成员。 可以添加自定义特性来指定所需的其他任何信息。 有关详细信息,请参阅创建自定义特性 (C#)
  • 可以将一个或多个特性应用于整个程序集、模块或较小的程序元素(如类和属性)。
  • 特性可以像方法和属性一样接受自变量。
  • 程序可使用反射来检查自己的元数据或其他程序中的元数据。 有关详细信息,请参阅使用反射访问特性 (C#)

使用特性

可以将特性附加到几乎任何声明中,尽管特定特性可能会限制可有效附加到的声明的类型。 在 C# 中,通过用方括号 ([]) 将特性名称括起来,并置于应用该特性的实体的声明上方以指定特性。

在此示例中,SerializableAttribute 特性用于将具体特征应用于类:

[Serializable]
public class SampleClass
{
    // Objects of this type can be serialized.
}

下方示例声明了一个具有特性 DllImportAttribute 的方法:

[System.Runtime.InteropServices.DllImport("user32.dll")]
extern static void SampleMethod();

如下方示例所示,可以将多个特性附加到声明中:

using System.Runtime.InteropServices;
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 方法或 getset 属性访问器
param 方法参数或 set 属性访问器参数
property 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 (C#)

特性的常见用途

下面列出了代码中特性的一些常见用途:

  • 在 Web 服务中使用 WebMethod 特性标记方法,以指明方法应可通过 SOAP 协议进行调用。 有关详细信息,请参阅 WebMethodAttribute
  • 描述在与本机代码互操作时如何封送方法参数。 有关详细信息,请参阅 MarshalAsAttribute
  • 描述类、方法和接口的 COM 属性。
  • 使用 DllImportAttribute 类调用非托管代码。
  • 从标题、版本、说明或商标方面描述程序集。
  • 描述要序列化并暂留类的哪些成员。
  • 描述如何为了执行 XML 序列化在类成员和 XML 节点之间进行映射。
  • 描述的方法的安全要求。
  • 指定用于强制实施安全规范的特征。
  • 通过实时 (JIT) 编译器控制优化,这样代码就一直都易于调试。
  • 获取方法调用方的相关信息。

有关详细信息,请参见:

请参阅