当前位置: 首页 > news >正文

C#—【特性详解以及自定义特性和通过反射读取特性】

 C#—【特性详解以及自定义特性和通过反射读取特性】

目录

 C#—【特性详解以及自定义特性和通过反射读取特性】

介绍:

特性可以用于多种用途,包括但不限于:

特性的分类:

特性的主要用途(举一些直观例子)

1. 标记代码,让框架自动处理

2. 数据验证(表单/模型校验)

3. 控制序列化行为

4. 生成文档(如API接口说明)

5. 实现AOP(面向切面编程)

为什么用特性而不用其他方法?

日常开发中的高频场景

一句话总结

常见预定义特性(Predefined Attributes)

示例代码

自定义特性(Custom Attribute)

一、如何定义自定义特性?

二、如何应用自定义特性?

三、如何通过反射读取特性?

总结

自定义特性和反射的应用实例


介绍:

‌C#中的特性(Attribute)主要用于向程序元素(如类、方法、属性等)添加元数据,提供关于这些元素的附加信息,这些信息可以在编译和运行时被访问‌‌。

特性的核心作用是为代码添加额外的描述信息(元数据),从而在不修改代码逻辑的前提下,动态控制程序行为。它像是给代码贴“标签”,让其他代码或框架能通过反射“读懂”这些标签,实现灵活的功能扩展。

特性可以用于多种用途,包括但不限于:

  • ‌元数据提供‌:特性可以用来提供关于程序元素的附加信息,这些信息可以在编译和运行时被访问。例如,可以通过特性来描述类的用途、方法的执行条件等‌。
  • ‌自定义扩展‌:开发者可以自定义特性,用于特定的应用场景,如验证、序列化、日志记录等。自定义特性可以接受参数,提供更丰富的信息‌。
  • ‌反射应用‌:特性可以通过反射在运行时被访问,这使得它们在动态编程中非常有用。例如,可以使用反射来检查类的元数据或方法的行为信息‌。
  • 标记程序元素:特性可以用来标记程序元素,以便在编译时或运行时进行特定的处理。例如,使用 [Obsolete] 特性可以标记一个方法或类。

特性的分类:

预定义特性:由 .NET 框架内置,直接作用于代码元素,提供标准化功能。

自定义特性:由开发者定义,继承自 System.Attribute,用于扩展元数据标记。


特性的主要用途(举一些直观例子)

1. 标记代码,让框架自动处理
  • 场景:Web API 中标记接口的 HTTP 方法类型

    [HttpGet]  // 标记这是一个处理GET请求的方法
    public IActionResult GetUser(int id) { ... }
    • 作用:框架看到 [HttpGet] 后,自动将GET请求路由到该方法,无需手动配置路由表。

2. 数据验证(表单/模型校验)
  • 场景:用户注册时校验用户名是否合法

    public class User
    {
        [Required(ErrorMessage = "用户名不能为空")]  // 非空校验
        [MaxLength(20)]                           // 长度校验
        public string Name { get; set; }
    }
    • 作用:通过特性直接声明规则,校验逻辑由框架自动完成,代码更简洁。

3. 控制序列化行为
  • 场景:返回JSON数据时隐藏敏感字段
  • public class Product
    {
        public int Id { get; set; }
        
        [JsonIgnore]  // 序列化为JSON时忽略此字段
        public string InternalCode { get; set; }
    }
  • 作用:无需修改类的内部逻辑,直接通过特性控制输出结果。
4. 生成文档(如API接口说明)
  • 场景:用Swagger自动生成API文档

    [ApiController]
    [Route("api/[controller]")]
    public class UserController
    {
        [HttpPost("login")]
        [SwaggerOperation("用户登录接口")]  // 添加接口描述
        public IActionResult Login(...) { ... }
    }
    • 作用:通过特性添加注释,Swagger自动读取并生成可视化文档。

5. 实现AOP(面向切面编程)
  • 场景:自动记录方法执行耗时

    [LogExecutionTime]  // 标记需要记录耗时的方法
    public void ProcessData()
    {
        // 业务逻辑
    }
    
    // 特性背后的拦截器(伪代码)
    public class LogInterceptor
    {
        void Execute()
        {
            Stopwatch sw = Stopwatch.StartNew();
            // 调用原方法
            sw.Stop();
            Console.WriteLine($"耗时:{sw.Elapsed}");
        }
    }
  • 作用:通过特性标记,自动为方法添加日志、事务等通用逻辑,无需侵入业务代码。


为什么用特性而不用其他方法?

  1. 解耦:将配置信息与代码逻辑分离,修改特性标签即可调整行为,无需改动核心代码。

  2. 可扩展:框架或第三方库可通过反射读取特性,实现高度灵活的扩展。

  1. 可读性:特性以声明式的方式标注意图,代码更直观(比如 [Required] 比写一堆if判断更清晰)。


日常开发中的高频场景

  • Web开发:身份验证 ([Authorize])、路由 ([Route])、模型绑定 ([FromBody])

  • 数据校验:表单验证 ([Required][EmailAddress])

  • 序列化:JSON忽略字段 ([JsonIgnore])、XML节点命名 ([XmlElement("Name")])

  • 单元测试:标记测试方法 ([TestMethod])、测试分类 ([Category("Fast")])


一句话总结

特性就像代码的“便签纸”——通过声明式标签,让框架或其他工具自动帮你干活,既减少重复代码,又提升灵活性和可维护性。(这里的框架是指MVC,MVVM,EF这些,或者是 .NET框架内置的)


常见预定义特性(Predefined Attributes)

由 .NET 框架内置,直接作用于代码元素,提供标准化功能。

特性名称作用目标用途说明
[Serializable]标记类可被序列化(二进制、XML等)。
[Obsolete]方法、类等标记代码已过时,编译时警告或报错。
[DllImport]方法声明调用非托管 DLL 中的方法(如系统 API)。
[Conditional]方法控制方法是否在特定条件下被编译(常用于调试)。
[Authorize]类、方法 (ASP.NET)限制访问权限(如身份验证)。
[Required]属性 (数据验证)标记字段为必填项(常用于模型验证)。
[Test]方法 (单元测试)标记方法为单元测试方法(如 MSTest/NUnit)。
示例代码
// 标记类可序列化
[Serializable]
public class UserData { /* ... */ }

// 标记方法已过时,并提示替代方案
[Obsolete("请改用 NewMethod()", true)]
public void OldMethod() { /* ... */ }

// 调用Windows API
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int MessageBox(int hWnd, string text, string caption, int type);

// 条件编译方法(仅DEBUG模式下生效)
[Conditional("DEBUG")]
public void LogDebugInfo() { /* ... */ }


自定义特性(Custom Attribute)

在 C# 中,自定义特性(Custom Attribute) 是一种通过继承 System.Attribute 类来创建的、可附加到代码元素(类、方法、属性等)上的元数据标记。它的核心价值是 通过声明式编程为代码添加额外的描述信息,供其他代码(如框架或工具)在运行时通过反射动态读取和处理。

一、如何定义自定义特性?

继承 Attribute 类
创建一个类并继承 System.Attribute,类名建议以 Attribute 结尾(如 MyCustomAttribute),但使用时可以省略后缀(如 [MyCustom])。

指定作用目标
通过 AttributeUsage 特性明确该自定义特性可以应用在哪些代码元素上(如类、方法、属性等)。

[AttributeUsage(
    AttributeTargets.Class | AttributeTargets.Method, // 可作用在类或方法上
    AllowMultiple = false,                            // 是否允许重复应用
    Inherited = true                                   // 是否可被派生类继承
)]
public class AuthorAttribute : Attribute
{
    public string Name { get; }
    public DateTime CreatedDate { get; }

    // 构造函数
    public AuthorAttribute(string name)
    {
        Name = name;
        CreatedDate = DateTime.Now;
    }
}

二、如何应用自定义特性?

将特性以 [特性名(参数)] 的形式附加到代码元素上:

[Author("Alice", CreatedDate = "2023-10-01")] // 附加到类
public class MyController
{
    [Author("Bob")]                           // 附加到方法
    public void ProcessData()
    {
        // 方法逻辑
    }
}

三、如何通过反射读取特性?

在运行时,通过反射获取代码元素上的特性实例,并提取信息:

// 获取类上的 Author 特性
var classAttributes = typeof(MyController)
    .GetCustomAttributes(typeof(AuthorAttribute), false);

if (classAttributes.Length > 0)
{
    var author = (AuthorAttribute)classAttributes[0];
    Console.WriteLine($"类作者: {author.Name}, 创建时间: {author.CreatedDate}");
}

// 获取方法上的 Author 特性
var methodInfo = typeof(MyController).GetMethod("ProcessData");
var methodAttributes = methodInfo.GetCustomAttributes(typeof(AuthorAttribute), false);

if (methodAttributes.Length > 0)
{
    var author = (AuthorAttribute)methodAttributes[0];
    Console.WriteLine($"方法作者: {author.Name}");
}


总结

  • 预定义特性是 .NET 生态的“瑞士军刀”,覆盖常见标准化需求。
  • 自定义特性是“万能工具箱”,用于扩展个性化元数据逻辑。
  • 两者结合使用,既能利用框架能力,又能灵活扩展业务逻辑。


自定义特性和反射的应用实例

效果:参考系统的特性实现自己的特性,该特性可以用于记录程序的编写情况

namespace 特性
{
    public partial class Form1 : Form
    {
        // 实例: 参考系统的特性实现自己的特性,该特性可以用于记录程序的编写情况?

        public Form1()
        {
            InitializeComponent();

            // 使用反射机制获取类以及成员上的特性
            Type type = typeof(Test);
            // 判断类上是否有指定的特性标注
            if (type.IsDefined(typeof(RecordAttribute),false))
            {
                object[] objs = type.GetCustomAttributes(typeof(RecordAttribute),false);
                Console.WriteLine("------------------类上的特性标注---------------");
                foreach (RecordAttribute attribute in objs)
                {
                    Console.WriteLine(attribute);
                    Console.WriteLine(attribute.Type);
                    Console.WriteLine(attribute.Author);
                    Console.WriteLine(attribute.DateTime);
                    Console.WriteLine(attribute.Desc);
                }
            }

            // 获取函数上标注的特性
            MethodInfo method = type.GetMethod("Print", new Type[] { });
            Console.WriteLine("------------------函数的特性标注---------------");
            if (method.IsDefined(typeof(RecordAttribute), false))
            {
                object[] objs = method.GetCustomAttributes(typeof(RecordAttribute), false);
                foreach (RecordAttribute attribute in objs)
                {
                    Console.WriteLine(attribute);
                    Console.WriteLine(attribute.Type);
                    Console.WriteLine(attribute.Author);
                    Console.WriteLine(attribute.DateTime);
                    Console.WriteLine(attribute.Desc);
                }
            }
        }
    }

    // 定义一个类用于测试特性
    [Record("新增","jack","2050-11-10","该类主要用于测试自定义的特性")]
    [Record("更新", "peter", "2051-10-13", "将类名修改为Test")]
    class Test
    {
        [Record()]
        private string name;
        [Record()]
        public string Name { get => name; set => name = value; }

        [Record("修改","lucy","2025-1-1","该方法修改后用于输出函数自己")]
        public void Print()
        {
            Console.WriteLine("print().");
        }
    }

    /// 自定义特性
    // 1-定义一个类继承自Attribute
    [AttributeUsage(AttributeTargets.Class|AttributeTargets.Field
        |AttributeTargets.Property|AttributeTargets.Method,
        AllowMultiple =true)]
    class RecordAttribute : Attribute
    {
        // 2-编写类成员
        private string type;  // 操作类型: 新增、更新
        private string author; // 作者
        private string dateTime; // 日期
        private string desc; // 描述

        public RecordAttribute(){ }

        public RecordAttribute(string type, string author, string dateTime, string desc)
        {
            this.type = type;
            this.author = author;
            this.dateTime = dateTime;
            this.desc = desc;
        }

        public string Type { get => type; set => type = value; }
        public string Author { get => author; set => author = value; }
        public string DateTime { get => dateTime; set => dateTime = value; }
        public string Desc { get => desc; set => desc = value; }
    }
}

结果

------------------类上的特性标注---------------
特性.RecordAttribute
新增
jack
2050-11-10
该类主要用于测试自定义的特性
特性.RecordAttribute
更新
peter
2051-10-13
将类名修改为Test
------------------函数的特性标注---------------
特性.RecordAttribute
修改
lucy
2025-1-1
该方法修改后用于输出函数自己

相关文章:

  • 架构师面试(十八):I/O 原理
  • 前端开发:这就是终点吗?
  • CXL协议之FM(Fabric Management)解释
  • HTML 列表
  • 3.17 配置hadoop集群-理论准备
  • MySql补充
  • 20250319在荣品的PRO-RK3566开发板的buildroot系统下使用集成的QT应用调试串口UART3
  • 大模型学习-从零开始在colab训练大模型
  • ffmpeg库视频硬解码使用流程
  • R语言基础小测验
  • 什么是 HTML 实体,常见的 HTML 实体有哪些用途?
  • Qt带参数的信号和槽,以及信号与槽的连接方式
  • AI 时代,学习 Java 应如何入手?
  • Ubuntu-server-16.04 设置多个ip和多个ipv6
  • 传统服务部署、虚拟化部署与云原生部署资源消耗对比与优化指南
  • nvm 安装某个node.js版本后不能使用或者报错,或不能使用npm的问题
  • 从 Snowflake 到 Databend Cloud:全球游戏平台借助 Databend 实现实时数据处理
  • AMBA-CHI协议详解(二十五)
  • C的输入输出深入解析
  • Jobby、Quarkus 和 Spring Boot对比
  • 东莞建网站公司动/网络推广的方法
  • 济南网站建设推广服务/关于进一步优化落实疫情防控措施
  • 中标查询/青岛seo招聘
  • 海口快速建站公司推荐/危机舆情公关公司
  • 外贸出口网站建设/百度应用市场
  • 排名网站优化培训/seo建站工具