C# 特性(Attribute)

在 .NET Core 的开发体系中,特性(Attribute)不仅仅是一个简单的标签,更是实现声明式编程、解耦业务逻辑与基础设施代码的核心机制。无论是数据验证、API 路由控制,还是复杂的面向切面编程(AOP),特性都提供了高效的解决方案。

1. 特性 Attribute

特性

从本质上讲,特性是一种将元数据(Metadata)附加到代码元素(如类、方法、属性、参数等)上的机制。它不会直接改变代码的执行逻辑,而是为编译器或运行时提供额外的描述性信息。

理解为给代码打的"标签"或"注解"。例如:

  • [Required] 告诉框架该字段不能为空。
  • [HttpGet] 告诉 ASP.NET Core 该方法仅响应 GET 请求。
  • [Serializable] 告诉序列化器该对象可以被序列化。

在没有引入特性之前,开发者往往需要通过大量的 if-else 或 switch 语句来处理诸如数据库映射、JSON 序列化、数据验证等逻辑。这导致代码紧密耦合,维护困难。

引入特性后,实现了‌声明式编程‌的优势:

  • ‌解耦‌:业务代码专注于核心逻辑,横切关注点(如日志、验证、权限)通过特性分离。
  • ‌统一规则‌:框架通过扫描特性自动执行相应逻辑,确保行为一致。
  • **简洁性无需编写繁琐的基础设施代码,只需添加标签即可生效。

2.自定义特性

虽然 .NET 提供了丰富的内置特性,但在实际开发中,我们常需要根据业务需求创建自定义特性例如实现特定的日志记录、权限校验或缓存策略。

创建自定义特性通常遵循以下步骤:

  • 创建一个继承自 System.Attribute 的类。
  • 使用 AttributeUsage 指定特性的适用目标(类、方法、属性等)。
  • 定义构造函数和属性以传递参数。

自定义日志特性

需要一个特性,用于标记需要记录详细执行日志的方法。

cs 复制代码
using System;

// 1. 定义特性类
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
public class CustomLogAttribute : Attribute
{
    public string Message { get; set; }
    public LogLevel Level { get; set; } = LogLevel.Information;

    public CustomLogAttribute(string message)
    {
        Message = message;
    }
}

public enum LogLevel
{
    Information,
    Warning,
    Error
}

使用自定义特性

cs 复制代码
public class OrderService
{
    // 2. 应用特性
    [CustomLog("开始处理订单创建", Level = LogLevel.Information)]
    public void CreateOrder(Order order)
    {
        // 业务逻辑
        Console.WriteLine($"Order {order.Id} created.");
    }
}

此时,CustomLogAttribute 仅仅是一个元数据标记。若要使其真正生效(即执行日志记录逻辑),通常需要结合‌反射 ‌或‌AOP拦截器‌。

3.扩展 AOP 应用

特性本身不执行代码,必须配合拦截机制才能实现面向切面编程(AOP)。在 .NET Core 中,实现 AOP 的主流方式包括使用动态代理库(如 Castle DynamicProxy)或依赖注入容器的拦截扩展(如 Autofac)。

AOP 的核心思想

AOP旨在将横切关注点(如日志、事务、异常处理)从业务逻辑中剥离。当调用被特性标记的方法时,流程如下:

  • 调用方触发方法。
  • 代理对象拦截调用。
  • 执行前置逻辑(如记录开始时间、检查权限)。
  • 执行真实业务方法。
  • 执行后置逻辑(如记录结束时间、捕获异常)。

结合 Castle DynamicProxy 实现日志拦截

以下示例展示如何通过实现 `IInterceptor接口,读取自定义特性并执行日志记录。

定义拦截器

cs 复制代码
using Castle.DynamicProxy;
using System.Reflection;

public class LogInterceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        // 1. 获取方法上的自定义特性
        var method = invocation.MethodInvocationTarget;
        var logAttribute = method.GetCustomAttributes(typeof(CustomLogAttribute), true)
                                 .FirstOrDefault() as CustomLogAttribute;

        if (logAttribute != null)
        {
            // 2. 前置逻辑:记录开始
            Console.WriteLine($"[LOG] {logAttribute.Message} - Start");
        }

        try
        {
            // 3. 执行真实方法
            invocation.Proceed();
        }
        catch (Exception ex)
        {
            // 4. 异常处理
            Console.WriteLine($"[ERROR] Exception occurred: {ex.Message}");
            throw;
        }
        finally
        {
            if (logAttribute != null)
            {
                // 5. 后置逻辑:记录结束
                Console.WriteLine($"[LOG] {logAttribute.Message} - End");
            }
        }
    }
}

创建代理并执行

cs 复制代码
var proxyGenerator = new ProxyGenerator();
var interceptor = new LogInterceptor();

// 创建 OrderService 的代理对象
var orderService = proxyGenerator.CreateClassProxy<OrderService>(interceptor);

var order = new Order { Id = 1001 };
orderService.CreateOrder(order);

// 输出结果:
// [LOG] 开始处理订单创建 - Start
// Order 1001 created.
// [LOG] 开始处理订单创建 - End
  • Autofac + Castle DynamicProxy‌:在 ASP.NET Core 项目中,常替换默认 DI 容器为 Autofac,利用其 EnableInterfaceInterceptors 或 EnableClassInterceptors 功能,自动为注册的服务生成代理,无需手动创建 ProxyGenerator。
  • DispatchProxy‌:.NET Core 内置的轻量级代理类,适用于简单的场景,但功能不如 Castle DynamicProxy 强大。

4.常用内置特性分类

.NET Core 框架内置了大量特性,覆盖了 Web 开发、数据验证、依赖注入等多个领域。熟练掌握这些特性能极大提升开发效率。

API 与 MVC 控制

这些特性主要用于 ASP.NET Core Web API 和 MVC 控制器,定义路由、HTTP 方法和行为。

特性 说明 示例
[ApiController] 标记控制器为 API 风格,启用自动模型验证和错误响应。 [ApiController]
[Route] 定义路由模板。 [Route("api/[controller]")]
[HttpGet] / [HttpPost] 指定允许的 HTTP 谓词。 [HttpGet("{id}")]
[FromBody] 指示参数从请求正文中绑定。 public IActionResult Post([FromBody] User user)
[FromQuery] 指示参数从查询字符串中绑定。 public IActionResult Get([FromQuery] int page)

数据验证特性

主要用于 DTO(数据传输对象)或实体模型,配合模型验证中间件使用。

特性 说明 示例
[Required] 字段不能为 null 或空。 [Required(ErrorMessage = "姓名必填")]
[StringLength] 限制字符串长度。 [StringLength(100)]
[Range] 限制数值范围。 [Range(1, 120)]
[EmailAddress] 验证邮箱格式。 [EmailAddress]
[RegularExpression] 使用正则表达式验证。 [RegularExpression(@"^\d+$")]

数据实体特性 (Entity Framework Core)

用于配置数据库映射关系。

特性 说明 示例
[Key] 指定主键。 [Key]
[Table] 指定对应的数据库表名。 [Table("Users")]
[Column] 指定列名或类型。 [Column("user_name")]
[NotMapped] 排除属性,不映射到数据库。 [NotMapped]
[DatabaseGenerated] 指定数据库生成策略(如自增)。 [DatabaseGenerated(DatabaseGeneratedOption.Identity)]

依赖注入与服务生命周期

引入了直接标记服务生命周期的特性,简化了注册过程。

特性 说明
[ServiceLifetime.Scoped] 标记服务为 Scoped 生命周期。
[ServiceLifetime.Singleton] 标记服务为 Singleton 生命周期。
[ServiceLifetime.Transient] 标记服务为 Transient 生命周期。

PS:使用这些特性时,通常需要在程序启动时扫描程序集并自动注册服务。

安全与授权

特性 说明
[Authorize] 要求用户经过身份验证才能访问。
[AllowAnonymous] 允许匿名访问,通常用于覆盖全局授权策略。
[ValidateAntiForgeryToken] 防止跨站请求伪造攻击。

总结

特性(Attribute)是 .NET Core 生态中连接元数据与运行时行为的桥梁。

  • ‌理解本质‌:特性是元数据,需配合反射或拦截器才能产生实际效果。
  • ‌自定义扩展 ‌:通过继承 Attribute 类,可以轻松扩展业务所需的标记能力。
  • ‌AOP 实战‌:结合 Castle DynamicProxy 或 Autofac,利用特性实现日志、事务、权限等横切逻辑的自动化处理,显著提升代码的可维护性。
  • ‌善用内置‌:熟练掌握系统内置的特性,能够以更少的代码实现标准化的 Web 开发、数据验证和 ORM 映射。
相关推荐
IT规划师2 小时前
Redis 数据类型全解 - Bitmap 生产实战:把位视图讲准、用对、守住红线(六)
redis·.net
淡海水20 小时前
10-01-互操作-C#与C++边界的内存管理
unity·c#·游戏引擎·.netcore·gc
WarPigs1 天前
C#把byte[]以十六进制打印,带分割符
c#
暴走约伯1 天前
【VFX工具链源码拆解 04】拯救丢失的绑定:Timeline 一键克隆与层级相对路径重映射算法
unity·c#·游戏引擎·硬件工程
1-1=01 天前
C# 反射
c#·.net·.netcore
一叶星殇1 天前
版本即契约:.NET 6 WebAPI 多版本治理与 Swagger 文档分治实践
mvc·.net
Jeff-Tsai1 天前
Day 9:数据与定义分家,表单不必各自建类型
.net·架构设计·erp·definition-driven
步步为营DotNet1 天前
.NET 可观测性选型:Metrics、ActivitySource、EventSource 到底该用哪个
c#·.net·metrics·可观测性
CSharp精选营1 天前
我把 Ant Design Blazor 翻了一遍:Blazor 中后台组件库到底能不能用
.net·blazor·ant design blazor·中后台系统·mudblazor