====== C# 特性 (Attribute) 详解 ======
在 C# 中,**特性 (Attribute)** 是一种强大的机制,允许开发者向程序集、模块、类型、成员等添加声明性信息(元数据)。程序可以在运行时通过**反射 (Reflection)** 来读取这些信息,从而改变程序的行为。
===== 1. 什么是特性? =====
特性本质上是一个类,它们继承自 system:attribute 类。它们就像是贴在代码元素(类、方法、属性等)上的“标签”或“备注”。
* **元数据**:特性本身不直接改变代码的逻辑,而是提供关于代码的额外信息。
* **被动性**:特性通常是被动存在的,需要有外部代码(如编译器、框架或你自己的反射代码)去主动读取并根据它做出反应。
===== 2. 常见的使用场景 =====
特性在 .NET 开发中无处不在,以下是一些典型的应用场景:
* **序列化控制**:控制对象如何转换为 JSON 或 XML。
* 例如:`[JsonIgnore]` (Newtonsoft.Json), `[JsonPropertyName("id")]` (System.Text.Json), `[BsonIgnore]` (MongoDB)。
* **数据库映射 (ORM)**:定义类与数据库表的对应关系。
* 例如:`[Table("Users")]`, `[Key]`, `[Required]` (Entity Framework)。
* **编译器指令**:告诉编译器如何处理代码。
* 例如:`[Obsolete]` (标记过时), `[Conditional]` (条件编译)。
* **测试框架**:标识测试方法。
* 例如:`[Test]`, `[SetUp]` (NUnit), `[TestMethod]` (MSTest)。
* **Web API 路由与验证**:
* 例如:`[HttpGet]`, `[Route("api/users")]`, `[Authorize]`。
===== 3. 基本语法 =====
特性使用方括号 `[]` 包裹,放置在目标元素(类、方法、属性等)的上方。
using System;
// 标记这个类是可序列化的
[Serializable]
public class User
{
// 标记这个方法已经过时,如果使用会产生警告
[Obsolete("请使用 NewLogin 方法代替")]
public void OldLogin()
{
Console.WriteLine("Old Login");
}
public void NewLogin()
{
Console.WriteLine("New Login");
}
}
===== 4. 自定义特性 =====
你可以通过继承 `System.Attribute` 类来创建自己的特性。
==== 4.1 定义特性类 ====
通常特性类名以 `Attribute` 结尾(虽然使用时可以省略)。
using System;
// 1. 继承 System.Attribute
// 2. (可选) 使用 AttributeUsage 限制该特性可以用在哪里
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class AuthorAttribute : Attribute
{
public string Name { get; }
public double Version { get; set; }
// 构造函数参数 -> 位置参数 (Positional Parameters)
public AuthorAttribute(string name)
{
Name = name;
Version = 1.0; // 默认值
}
}
==== 4.2 使用自定义特性 ====
// 使用时可以省略 "Attribute" 后缀
// "Alice" 是位置参数,Version = 1.1 是命名参数
[Author("Alice", Version = 1.1)]
public class PaymentService
{
[Author("Bob")]
public void Process()
{
// ...
}
}
===== 5. 通过反射读取特性 =====
特性定义好并贴上去后,如果没有代码去读取它,它就没有任何作用。我们需要使用 **反射 (Reflection)** 来获取这些信息。
using System;
using System.Reflection;
public class Program
{
public static void Main()
{
// 获取 PaymentService 的类型信息
Type type = typeof(PaymentService);
// 1. 获取类上的特性
// GetCustomAttribute 是扩展方法,需要 using System.Reflection;
var classAuth = type.GetCustomAttribute();
if (classAuth != null)
{
Console.WriteLine($"类作者: {classAuth.Name}, 版本: {classAuth.Version}");
}
// 2. 获取方法上的特性
MethodInfo method = type.GetMethod("Process");
var methodAuth = method.GetCustomAttribute();
if (methodAuth != null)
{
Console.WriteLine($"方法作者: {methodAuth.Name}");
}
}
}
===== 6. AttributeUsage 详解 =====
`AttributeUsage` 本身也是一个特性,用来修饰你自己定义的特性类。它主要控制三个方面:
^ 属性 ^ 描述 ^
| **ValidOn** | 指定该特性可以应用在哪些程序元素上(类、方法、属性、字段等)。使用 `AttributeTargets` 枚举。 |
| **AllowMultiple** | 是否允许在同一个元素上多次使用该特性。默认为 `false`。 |
| **Inherited** | 派生类是否继承基类的该特性。默认为 `true`。 |
**示例:**
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class ColumnAttribute : Attribute { ... }
===== 7. 总结 =====
* **定义**:继承自 `System.Attribute`。
* **语法**:`[AttributeName(参数)]`。
* **核心**:特性是静态的元数据,必须通过**反射**机制在运行时读取才能发挥作用。
* **区别**:不要与 Python 的装饰器(Decorator)混淆,C# 特性不直接修改代码逻辑,而是提供信息供解释器使用。