跳到主要内容

C# 特性(Attributes)

特性(Attributes)是 C# 中一种强大的元数据机制,它允许开发者为代码元素(类、方法、属性、参数等)添加声明性信息。这些元数据可以在编译时被编译器读取,也可以在运行时通过反射获取,从而实现各种强大的功能。

特性概述

什么是特性?

特性是一种声明性标签,用于为程序元素添加元数据。可以把特性理解为"代码的注释",但与普通注释不同的是,特性的内容可以在运行时被程序读取和使用。

特性提供了一种将声明性信息与 C# 代码(类型、方法、属性等)关联的方式。它与程序元素的关联方式类似于在配置文件中添加信息,但更具结构化,且与代码本身紧密绑定。

特性的特点

  • 元数据载体:特性本质上是向程序元素添加元数据
  • 编译时处理:编译器可以识别某些特性并改变编译行为
  • 运行时读取:通过反射机制可以在运行时获取特性信息
  • 类型安全:特性是类型化的,继承自 System.Attribute

特性语法

在 C# 中,特性使用方括号 [] 包裹,放置在被修饰元素的前面:

// 特性名可以省略 "Attribute" 后缀
[Serializable]
public class Person
{
[Obsolete("请使用 GetFullName 方法")]
public string Name { get; set; }

[Range(0, 150)]
public int Age { get; set; }
}

// 完整写法(效果相同)
[SerializableAttribute]
public class Product { }

特性参数

特性可以接受参数,分为位置参数(构造函数参数)和命名参数(属性或字段):

// 位置参数必须按顺序指定,不能省略
[Obsolete("此方法已过时", true)] // true 表示编译错误

// 命名参数可选,可按任意顺序指定
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]

// 混合使用
[SomeAttribute("位置参数", NamedParam = "值")]

特性目标

默认情况下,特性应用于紧随其后的元素。但有时需要明确指定目标:

// 目标:值        应用范围
[assembly: AssemblyVersion("1.0.0.0")] // 整个程序集
[method: MyAttribute] // 方法
[return: MyAttribute] // 返回值
[param: MyAttribute] // 参数
[field: MyAttribute] // 字段(自动属性的后备字段)
[property: MyAttribute] // 属性

// 示例:为自动属性的后备字段添加特性
public class MyClass
{
// 特性应用于属性
[MyAttribute]
public string Name { get; set; }

// 特性应用于后备字段
[field: MyAttribute]
public string Description { get; set; }
}

AttributeUsage 特性

AttributeUsage 特性用于定义自定义特性可以应用的目标元素类型以及使用方式。这是创建自定义特性时最重要的配置特性。

AttributeUsage 参数

[AttributeUsage(
AttributeTargets.Class, // 有效目标
AllowMultiple = false, // 是否允许重复应用
Inherited = true // 是否可被继承
)]
public class MyAttribute : Attribute
{
}

AttributeTargets 枚举

AttributeTargets 定义了特性可以应用的目标类型:

目标说明
Assembly程序集
Module模块
Class
Struct结构体
Enum枚举
Constructor构造函数
Method方法
Property属性
Field字段
Event事件
Interface接口
Parameter参数
Delegate委托
ReturnValue返回值
GenericParameter泛型参数
All所有元素

多个目标可以使用 | 运算符组合:

// 可应用于类和方法
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DocumentAttribute : Attribute
{
public string Description { get; }

public DocumentAttribute(string description)
{
Description = description;
}
}

// 使用示例
[Document("用户管理服务")]
public class UserService
{
[Document("获取用户信息")]
public User GetUser(int id) => null;
}

AllowMultiple 属性

控制特性是否可以在同一元素上多次应用:

// 允许重复应用
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class LogAttribute : Attribute
{
public string Category { get; }
public LogAttribute(string category) => Category = category;
}

// 使用
public class DataProcessor
{
[Log("数据库")]
[Log("缓存")]
[Log("日志")]
public void Process() { }
}

Inherited 属性

控制特性是否可以被派生类继承:

// 不可继承
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class TableAttribute : Attribute
{
public string Name { get; }
public TableAttribute(string name) => Name = name;
}

[Table("Users")]
public class BaseEntity { }

// BaseEntity 的特性不会被继承
// TableAttribute 不会应用于 User
public class User : BaseEntity { }

内置特性

Obsolete 特性

Obsolete 特性用于标记不再推荐使用的程序元素,提醒开发者使用替代方案。

// 仅显示警告
[Obsolete("此方法已弃用,请使用 NewMethod")]
public void OldMethod() { }

// 编译时报错
[Obsolete("此方法已被移除,请使用 NewMethod", true)]
public void RemovedMethod() { }

// 使用 nameof 确保名称正确(C# 10+)
[Obsolete($"请使用 {nameof(NewMethod)} 代替")]
public void DeprecatedMethod() { }

public void NewMethod() { }

Conditional 特性

Conditional 特性使方法的执行依赖于预处理符号。常用于调试和测试场景:

#define DEBUG
#define TRACE
using System.Diagnostics;

public class Logger
{
// 仅在定义了 DEBUG 符号时执行
[Conditional("DEBUG")]
public static void DebugLog(string message)
{
Console.WriteLine($"[DEBUG] {message}");
}

// 仅在定义了 TRACE 符号时执行
[Conditional("TRACE")]
public static void TraceLog(string message)
{
Console.WriteLine($"[TRACE] {message}");
}

// 多个条件(OR 关系)
[Conditional("DEBUG"), Conditional("TEST")]
public static void DebugOrTestLog(string message)
{
Console.WriteLine($"[DEBUG/TEST] {message}");
}
}

// 使用
Logger.DebugLog("调试信息"); // DEBUG 定义时输出
Logger.TraceLog("追踪信息"); // TRACE 定义时输出

注意Conditional 特性的方法必须返回 void,且不能标记为 override

Serializable 特性

Serializable 特性表示类可以序列化:

[Serializable]
public class Person
{
public string Name { get; set; }
public int Age { get; set; }

// 不序列化此字段
[NonSerialized]
private string _temporaryCache;
}

// 序列化示例
var person = new Person { Name = "张三", Age = 25 };
using var stream = File.OpenWrite("person.dat");
var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
formatter.Serialize(stream, person);

DllImport 特性

DllImport 特性用于调用非托管的 DLL 函数(P/Invoke):

using System.Runtime.InteropServices;

public class NativeMethods
{
// 调用 user32.dll 中的 MessageBox 函数
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int MessageBox(IntPtr hWnd, string text, string caption, int options);

// 调用 kernel32.dll 中的 Sleep 函数
[DllImport("kernel32.dll")]
public static extern void Sleep(uint milliseconds);

// 设置调用约定
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int puts(string str);
}

// 使用
NativeMethods.MessageBox(IntPtr.Zero, "Hello", "提示", 0);
NativeMethods.Sleep(1000);

Caller Info 特性

这些特性用于获取调用者的信息,常用于日志和诊断:

using System.Runtime.CompilerServices;

public class Tracer
{
public static void Log(
string message,
[CallerMemberName] string memberName = "",
[CallerFilePath] string filePath = "",
[CallerLineNumber] int lineNumber = 0)
{
Console.WriteLine($"[{Path.GetFileName(filePath)}:{lineNumber}] {memberName}: {message}");
}
}

public class OrderService
{
public void CreateOrder(string productId)
{
// 无需手动传递参数
// 自动填充: memberName="CreateOrder", filePath=当前文件路径, lineNumber=当前行号
Tracer.Log($"创建订单: {productId}");
}
}

其他常用内置特性

// 结构体布局控制
[StructLayout(LayoutKind.Sequential)]
public struct Point
{
public int X;
public int Y;
}

// 指定默认值
[DefaultValue(true)]
public bool IsEnabled { get; set; }

// 忽略序列化
[JsonIgnore]
public string Password { get; set; }

// XML 序列化控制
[XmlElement("UserName")]
public string Name { get; set; }

[XmlIgnore]
public string InternalId { get; set; }

// 组件模型
[Browsable(false)] // 属性浏览器中隐藏
[DisplayName("用户名称")]
[Description("用户的显示名称")]
[Category("基本信息")]
public string DisplayName { get; set; }

自定义特性

创建自定义特性

自定义特性必须继承自 System.Attribute

// 定义特性
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
AllowMultiple = false,
Inherited = true)]
public class ApiEndpointAttribute : Attribute
{
// 位置参数(构造函数)
public string Route { get; }

// 命名参数(属性)
public string Description { get; set; }
public string Version { get; set; } = "1.0";
public HttpMethod Method { get; set; } = HttpMethod.Get;

public ApiEndpointAttribute(string route)
{
Route = route;
}
}

// 使用自定义特性
[ApiEndpoint("/api/users", Description = "用户管理接口", Method = HttpMethod.Post)]
public class UserController
{
[ApiEndpoint("/{id}", Description = "获取用户详情")]
public User GetUser(int id) => null;
}

特性参数类型限制

特性参数只能是以下类型:

  • 基本类型:bool, byte, char, short, int, long, float, double
  • string
  • System.Type
  • 枚举类型
  • 以上类型的一维数组
// 合法的特性参数类型
public class ValidAttribute : Attribute
{
public int Number { get; set; } // 基本类型
public string Text { get; set; } // 字符串
public Type DataType { get; set; } // Type
public HttpStatusCode Status { get; set; } // 枚举
public int[] Numbers { get; set; } // 数组
}

// 非法的特性参数类型
public class InvalidAttribute : Attribute
{
// public User User { get; set; } // 错误:不能使用自定义类型
// public List<int> List { get; set; } // 错误:不能使用 List
}

泛型特性(C# 11+)

从 C# 11 开始,特性可以使用泛型:

// C# 11+ 泛型特性
[AttributeUsage(AttributeTargets.Property)]
public class ValidateAttribute<T> : Attribute
{
public T MinValue { get; }
public T MaxValue { get; }

public ValidateAttribute(T minValue, T maxValue)
{
MinValue = minValue;
MaxValue = maxValue;
}
}

// 使用泛型特性
public class Product
{
[Validate<int>(0, 10000)]
public int Price { get; set; }

[Validate<double>(0.0, 100.0)]
public double Discount { get; set; }
}

ASP.NET Core 常用特性

ApiController 特性

[ApiController] 特性启用 API 特定的行为:

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
// 自动验证模型状态
// 自动推断参数来源
// 强制特性路由
}

启用行为

  1. 属性路由要求:必须使用 [Route] 特性
  2. 自动 HTTP 400 响应:模型验证失败自动返回 400
  3. 绑定源参数推断:自动推断参数绑定方式
  4. 多部分表单数据推断:自动处理文件上传
  5. 错误状态码问题详情:返回 RFC 7807 标准格式

路由特性

[ApiController]
[Route("api/[controller]")] // 控制器级别路由
public class UsersController : ControllerBase
{
// GET api/users
[HttpGet]
public IEnumerable<User> GetAll() => _users;

// GET api/users/5
[HttpGet("{id:int}")]
public User GetById(int id) => _users.FirstOrDefault(u => u.Id == id);

// GET api/users/search?name=zhang
[HttpGet("search")]
public User Search([FromQuery] string name) => null;

// POST api/users
[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult<User> Create([FromBody] User user)
{
_users.Add(user);
return CreatedAtAction(nameof(GetById), new { id = user.Id }, user);
}

// PUT api/users/5
[HttpPut("{id}")]
public IActionResult Update(int id, [FromBody] User user) => NoContent();

// DELETE api/users/5
[HttpDelete("{id}")]
public IActionResult Delete(int id) => NoContent();

// 多个 HTTP 方法
[AcceptVerbs("GET", "POST")]
[Route("check")]
public IActionResult Check() => Ok();
}

参数绑定特性

public class OrdersController : ControllerBase
{
// [FromBody] - 从请求体绑定
[HttpPost]
public IActionResult Create([FromBody] Order order) => Ok();

// [FromQuery] - 从查询字符串绑定
[HttpGet]
public IActionResult List(
[FromQuery] int page = 1,
[FromQuery] int pageSize = 10) => Ok();

// [FromRoute] - 从路由数据绑定
[HttpGet("{id}")]
public IActionResult GetById([FromRoute] int id) => Ok();

// [FromHeader] - 从请求头绑定
[HttpGet]
public IActionResult GetByToken([FromHeader] string authorization) => Ok();

// [FromForm] - 从表单数据绑定
[HttpPost("upload")]
public IActionResult Upload([FromForm] IFormFile file) => Ok();

// [FromServices] - 从依赖注入容器绑定
[HttpGet]
public IActionResult GetTime([FromServices] ITimeService timeService)
=> Ok(timeService.Now);

// 组合使用
[HttpPut("{id}")]
public IActionResult Update(
[FromRoute] int id,
[FromBody] UpdateRequest request,
[FromHeader(Name = "X-Request-Id")] string requestId) => Ok();
}

过滤器特性

// 操作过滤器
[ServiceFilter(typeof(LoggingFilter))]
public class ProductsController : ControllerBase
{
// 异常处理
[HandleError(ExceptionType = typeof(NotFoundException), StatusCode = 404)]
[HttpGet("{id}")]
public IActionResult Get(int id) => Ok();

// 授权
[Authorize(Roles = "Admin")]
[HttpPost]
public IActionResult Create([FromBody] Product product) => Ok();

// 允许匿名访问
[AllowAnonymous]
[HttpGet("public")]
public IActionResult GetPublic() => Ok();

// 缓存
[ResponseCache(Duration = 60)]
[HttpGet("cached")]
public IActionResult GetCached() => Ok();

// 限流
[RateLimit(PermitLimit = 10, Window = 60)]
[HttpPost]
public IActionResult Create() => Ok();
}

// 自定义过滤器特性
public class LoggingAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
Console.WriteLine($"执行前: {context.ActionDescriptor.DisplayName}");
}

public override void OnActionExecuted(ActionExecutedContext context)
{
Console.WriteLine($"执行后: {context.ActionDescriptor.DisplayName}");
}
}

响应格式特性

// 指定响应类型
[Produces("application/json")]
[Consumes("application/json")]
public class ApiController : ControllerBase
{
// 指定可能的响应
[HttpGet("{id}")]
[ProducesResponseType(typeof(User), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public IActionResult Get(int id)
{
var user = _userService.GetById(id);
if (user == null)
return NotFound();
return Ok(user);
}

// 多种响应格式
[HttpGet("{id}")]
[Produces("application/json", "application/xml")]
public User Get(int id) => _userService.GetById(id);

// 指定请求格式
[HttpPost]
[Consumes("application/json")]
public IActionResult Create([FromBody] User user) => Ok();
}

Entity Framework Core 常用特性

实体映射特性

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

// 表映射
[Table("Users", Schema = "Security")]
public class User
{
// 主键
[Key]
public int Id { get; set; }

// 列映射
[Column("UserName", TypeName = "nvarchar(100)")]
[Required(ErrorMessage = "用户名不能为空")]
[StringLength(100, MinimumLength = 2)]
public string Name { get; set; }

// 唯一索引
[Index(IsUnique = true)]
[EmailAddress]
public string Email { get; set; }

// 不映射到数据库
[NotMapped]
public string ConfirmPassword { get; set; }

// 外键关系
[ForeignKey("Department")]
public int DepartmentId { get; set; }

public Department Department { get; set; }

// 时间戳(并发控制)
[Timestamp]
public byte[] RowVersion { get; set; }

// 并发令牌
[ConcurrencyCheck]
public string Code { get; set; }

// 默认值
[DefaultValue(true)]
public bool IsActive { get; set; }

// 数据库生成
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public DateTime CreatedAt { get; set; }

[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string FullName { get; set; }
}

验证特性

public class RegisterRequest
{
[Required(ErrorMessage = "用户名必填")]
[StringLength(50, MinimumLength = 3, ErrorMessage = "用户名长度必须在3-50之间")]
[RegularExpression(@"^[a-zA-Z0-9_]+$", ErrorMessage = "只能包含字母、数字和下划线")]
public string Username { get; set; }

[Required]
[EmailAddress(ErrorMessage = "邮箱格式不正确")]
public string Email { get; set; }

[Required]
[StringLength(100, MinimumLength = 8)]
[DataType(DataType.Password)]
public string Password { get; set; }

[Compare("Password", ErrorMessage = "两次密码不一致")]
public string ConfirmPassword { get; set; }

[Range(18, 120, ErrorMessage = "年龄必须在18-120之间")]
public int Age { get; set; }

[Phone]
public string Phone { get; set; }

[Url]
public string Website { get; set; }

[CreditCard]
public string CreditCard { get; set; }

[Range(typeof(DateTime), "1900-01-01", "2024-12-31")]
public DateTime BirthDate { get; set; }
}

复合主键和索引

// 复合主键(使用 Fluent API 更常见)
public class OrderDetail
{
[Key]
[Column(Order = 0)]
public int OrderId { get; set; }

[Key]
[Column(Order = 1)]
public int ProductId { get; set; }

public int Quantity { get; set; }
}

// 索引
[Index(nameof(LastName), nameof(FirstName))]
[Index(nameof(Email), IsUnique = true)]
public class Customer
{
public int Id { get; set; }

public string FirstName { get; set; }

public string LastName { get; set; }

public string Email { get; set; }
}

// 自定义索引名称(EF Core 7+)
[Index(nameof(Name), Name = "IX_Products_Name")]
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}

特性与反射结合使用

读取特性信息

// 定义特性
[AttributeUsage(AttributeTargets.Property)]
public class ColumnInfoAttribute : Attribute
{
public string Name { get; }
public bool IsRequired { get; set; }
public int MaxLength { get; set; }

public ColumnInfoAttribute(string name)
{
Name = name;
}
}

// 应用特性
public class User
{
[ColumnInfo("user_name", IsRequired = true, MaxLength = 50)]
public string Name { get; set; }

[ColumnInfo("email", IsRequired = true)]
public string Email { get; set; }

[ColumnInfo("age")]
public int Age { get; set; }
}

// 读取特性
public static void ReadAttributes()
{
var type = typeof(User);

foreach (var prop in type.GetProperties())
{
var attr = prop.GetCustomAttribute<ColumnInfoAttribute>();
if (attr != null)
{
Console.WriteLine($"属性: {prop.Name}");
Console.WriteLine($" 列名: {attr.Name}");
Console.WriteLine($" 必填: {attr.IsRequired}");
Console.WriteLine($" 最大长度: {attr.MaxLength}");
}
}
}

实现简单的验证框架

// 定义验证特性
[AttributeUsage(AttributeTargets.Property)]
public class ValidateAttribute : Attribute
{
public virtual bool IsValid(object value) => true;
}

[AttributeUsage(AttributeTargets.Property)]
public class RequiredAttribute : ValidateAttribute
{
public override bool IsValid(object value) => value != null && !string.IsNullOrEmpty(value.ToString());
}

[AttributeUsage(AttributeTargets.Property)]
public class RangeAttribute : ValidateAttribute
{
public double Min { get; }
public double Max { get; }

public RangeAttribute(double min, double max)
{
Min = min;
Max = max;
}

public override bool IsValid(object value)
{
if (value is IComparable comparable)
{
return comparable.CompareTo(Min) >= 0 && comparable.CompareTo(Max) <= 0;
}
return false;
}
}

// 验证器
public static class Validator
{
public static (bool IsValid, List<string> Errors) Validate(object obj)
{
var errors = new List<string>();
var type = obj.GetType();

foreach (var prop in type.GetProperties())
{
var value = prop.GetValue(obj);

foreach (var attr in prop.GetCustomAttributes<ValidateAttribute>())
{
if (!attr.IsValid(value))
{
errors.Add($"{prop.Name} 验证失败");
}
}
}

return (errors.Count == 0, errors);
}
}

// 使用
var user = new User { Name = "", Age = 200 };
var (isValid, errors) = Validator.Validate(user);
if (!isValid)
{
foreach (var error in errors)
Console.WriteLine(error);
}

实现 ORM 映射

// 表名特性
[AttributeUsage(AttributeTargets.Class)]
public class TableAttribute : Attribute
{
public string Name { get; }
public TableAttribute(string name) => Name = name;
}

// 列名特性
[AttributeUsage(AttributeTargets.Property)]
public class ColumnAttribute : Attribute
{
public string Name { get; }
public bool IsPrimaryKey { get; set; }
public bool IsIdentity { get; set; }

public ColumnAttribute(string name) => Name = name;
}

// 实体定义
[Table("t_users")]
public class User
{
[Column("id", IsPrimaryKey = true, IsIdentity = true)]
public int Id { get; set; }

[Column("name")]
public string Name { get; set; }

[Column("create_time")]
public DateTime CreatedAt { get; set; }
}

// 简单的 SQL 生成器
public static class SqlGenerator
{
public static string GenerateInsert<T>(T entity)
{
var type = typeof(T);

// 获取表名
var tableAttr = type.GetCustomAttribute<TableAttribute>();
var tableName = tableAttr?.Name ?? type.Name;

// 获取列
var columns = new List<string>();
var values = new List<string>();

foreach (var prop in type.GetProperties())
{
var colAttr = prop.GetCustomAttribute<ColumnAttribute>();
if (colAttr?.IsIdentity == true) continue; // 跳过自增列

var colName = colAttr?.Name ?? prop.Name;
var value = prop.GetValue(entity);

columns.Add(colName);
values.Add(FormatValue(value));
}

return $"INSERT INTO {tableName} ({string.Join(", ", columns)}) VALUES ({string.Join(", ", values)})";
}

private static string FormatValue(object value)
{
return value switch
{
null => "NULL",
string s => $"'{s}'",
DateTime dt => $"'{dt:yyyy-MM-dd HH:mm:ss}'",
_ => value.ToString()
};
}
}

// 使用
var user = new User { Name = "张三", CreatedAt = DateTime.Now };
var sql = SqlGenerator.GenerateInsert(user);
Console.WriteLine(sql);
// 输出: INSERT INTO t_users (name, create_time) VALUES ('张三', '2024-01-15 10:30:00')

最佳实践

特性命名约定

  • 特性类名应以 Attribute 结尾
  • 使用时可以省略 Attribute 后缀
  • 使用 PascalCase 命名
// 定义
public class MyCustomAttribute : Attribute { }

// 使用(两种写法等效)
[MyCustom]
[MyCustomAttribute]
public class MyClass { }

特性设计原则

  1. 单一职责:每个特性应该只做一件事
  2. 合理使用参数:位置参数用于必需信息,命名参数用于可选配置
  3. 提供合理的默认值:减少使用者的配置负担
  4. 文档完善:为特性添加 XML 注释说明用途
/// <summary>
/// 标记 API 端点的路由信息
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ApiRouteAttribute : Attribute
{
/// <summary>
/// 路由模板
/// </summary>
public string Template { get; }

/// <summary>
/// HTTP 方法,默认为 GET
/// </summary>
public string Method { get; set; } = "GET";

/// <summary>
/// 是否需要授权,默认为 false
/// </summary>
public bool RequireAuth { get; set; }

/// <summary>
/// 创建 API 路由特性
/// </summary>
/// <param name="template">路由模板</param>
public ApiRouteAttribute(string template)
{
Template = template;
}
}

避免滥用特性

特性虽然强大,但不应过度使用:

// 不好的做法:用特性做业务逻辑
[RolePermission("Admin", "Manager", "Editor")]
public void UpdateDocument() { }

// 更好的做法:在代码中明确表达
public void UpdateDocument(User user)
{
if (!user.HasAnyRole("Admin", "Manager", "Editor"))
throw new UnauthorizedAccessException();
// 业务逻辑
}

小结

C# 特性是一种强大的元数据机制,本文介绍了:

  1. 特性基础:语法、参数、目标
  2. AttributeUsage:定义特性的使用范围和方式
  3. 内置特性ObsoleteConditionalSerializableDllImport
  4. ASP.NET Core 特性ApiController、路由、参数绑定、过滤器等
  5. Entity Framework 特性:表映射、验证、关系配置等
  6. 自定义特性:创建、读取、高级应用
  7. 最佳实践:命名、设计、使用原则

特性与反射的结合使用是 .NET 框架中许多高级功能的基础,如依赖注入、序列化、ORM 映射、AOP 等。掌握特性的使用,能够帮助开发者更好地理解和利用 .NET 生态系统的强大能力。

练习

  1. 创建一个 [Audit] 特性,用于记录方法调用的信息(方法名、参数、执行时间)
  2. 实现一个简单的数据验证框架,支持 RequiredRangeStringLength 验证
  3. 创建一个 [Cache] 特性,配合 AOP 实现方法级别的缓存
  4. 使用特性实现一个简单的命令行参数解析器