news 2026/6/24 0:15:35

C#特性(Attributes)详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C#特性(Attributes)详解

第一部分:特性是什么?(类比贴标签)

1.1 最简单的理解

想象一下你在图书馆看书,你可能会:

  • 在重要的页面贴书签(标记重要内容)

  • 在书封面上贴标签(如"新书"、"推荐")

  • 在书的扉页写备注(如"张三借阅")

C#特性就像这些书签、标签和备注,它们为代码添加额外的信息。

1.2 实际代码对比

csharp

// 没有特性:只有代码本身 public void Calculate() { int result = 1 + 2; Console.WriteLine(result); } // 有特性:为代码添加额外信息 [Obsolete("这个方法已弃用,请使用新版CalculateNew")] public void Calculate() { int result = 1 + 2; Console.WriteLine(result); }

上面代码中,[Obsolete]就像贴在方法上的"注意"标签,告诉开发者这个方法过时了。

第二部分:内置特性的实际应用

2.1 最常用的三个内置特性

示例1:Obsolete(过时警告)

csharp

class Calculator { // 旧版本 - 不推荐使用 [Obsolete("请使用新版Add方法", false)] public int AddOld(int a, int b) { return a + b; } // 新版本 public int Add(int a, int b) { return a + b; } } // 使用 class Program { static void Main() { Calculator calc = new Calculator(); int result = calc.AddOld(5, 3); // 这里会显示警告 int result2 = calc.Add(5, 3); // 这是推荐方式 } }
  • false参数:只是警告,代码还能运行

  • true参数:会报错,代码无法编译

示例2:Conditional(条件编译)

csharp

using System.Diagnostics; class Logger { [Conditional("DEBUG")] // 只在DEBUG模式下才执行 public void LogDebug(string message) { Console.WriteLine($"[DEBUG] {DateTime.Now}: {message}"); } public void LogInfo(string message) { Console.WriteLine($"[INFO] {DateTime.Now}: {message}"); } } // 使用 class Program { static void Main() { Logger logger = new Logger(); // 如果在DEBUG模式下编译,这行会执行 // 如果在RELEASE模式下编译,这行代码就像不存在一样 logger.LogDebug("程序启动"); // 这行无论什么模式都会执行 logger.LogInfo("程序启动"); } }

示例3:Serializable(序列化标记)

csharp

[Serializable] // 告诉.NET:这个类可以转换成字节流保存 public class Person { public string Name { get; set; } public int Age { get; set; } [NonSerialized] // 告诉.NET:这个字段不要保存 public string TemporaryData; } // 使用 class Program { static void Main() { Person person = new Person { Name = "张三", Age = 25, TemporaryData = "临时值" }; // 保存到文件(只能保存Name和Age,不会保存TemporaryData) // 读取时,TemporaryData会是null或默认值 } }

第三部分:如何创建自己的特性

3.1 最简单的自定义特性

csharp

// 第一步:创建特性类 // 1. 必须继承System.Attribute // 2. 按惯例类名以"Attribute"结尾 public class MyNoteAttribute : Attribute { // 可以有一些属性 public string Note { get; set; } public DateTime Created { get; set; } } // 第二步:使用特性 [MyNote(Note = "这是一个重要的类", Created = "2024-01-01")] public class ImportantClass { [MyNote(Note = "核心方法")] public void ImportantMethod() { Console.WriteLine("做一些重要的事情"); } }

3.2 添加一些控制

csharp

// 限制特性只能用于类和方法 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class AuthorInfoAttribute : Attribute { public string Author { get; } public string Version { get; set; } // 构造函数定义必需信息 public AuthorInfoAttribute(string author) { Author = author; } } // 使用 [AuthorInfo("张三", Version = "1.0")] public class Document { [AuthorInfo("李四")] public void Save() { Console.WriteLine("保存文档"); } // 下面这行会报错,因为特性不支持属性 // [AuthorInfo("王五")] // public string Title { get; set; } }

第四部分:如何读取和使用特性

4.1 基本读取方法

csharp

using System; using System.Reflection; // 定义特性 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class TodoAttribute : Attribute { public string Task { get; } public Priority Priority { get; set; } public TodoAttribute(string task) { Task = task; } } public enum Priority { Low, Medium, High } // 使用特性 [Todo("优化性能", Priority = Priority.High)] public class GameEngine { [Todo("添加错误处理")] public void LoadLevel(string levelName) { Console.WriteLine($"加载关卡: {levelName}"); } [Todo("实现物理碰撞", Priority = Priority.Medium)] public void UpdatePhysics() { Console.WriteLine("更新物理"); } } // 读取特性 class Program { static void Main() { // 获取类型 Type gameType = typeof(GameEngine); // 读取类上的Todo特性 var classTodos = gameType.GetCustomAttributes(typeof(TodoAttribute), false); foreach (TodoAttribute todo in classTodos) { Console.WriteLine($"类待办: {todo.Task}, 优先级: {todo.Priority}"); } Console.WriteLine("\n方法待办列表:"); // 读取所有方法上的Todo特性 foreach (MethodInfo method in gameType.GetMethods()) { var methodTodos = method.GetCustomAttributes(typeof(TodoAttribute), false); foreach (TodoAttribute todo in methodTodos) { Console.WriteLine($"方法: {method.Name}"); Console.WriteLine($" 任务: {todo.Task}"); Console.WriteLine($" 优先级: {todo.Priority}"); } } } }

4.2 实用的示例:验证用户输入

csharp

using System; using System.Reflection; // 验证特性 [AttributeUsage(AttributeTargets.Property)] public class ValidateAttribute : Attribute { public int MinLength { get; set; } public int MaxLength { get; set; } public bool Required { get; set; } } // 用户类 public class User { [Validate(Required = true, MinLength = 2, MaxLength = 50)] public string Name { get; set; } [Validate(Required = true, MinLength = 6)] public string Password { get; set; } [Validate(MinLength = 0, MaxLength = 120)] public int Age { get; set; } } // 验证器 public class Validator { public static List<string> Validate(object obj) { List<string> errors = new List<string>(); Type type = obj.GetType(); // 检查所有属性 foreach (PropertyInfo property in type.GetProperties()) { // 获取Validate特性 ValidateAttribute validate = property.GetCustomAttribute<ValidateAttribute>(); if (validate != null) { object value = property.GetValue(obj); string propertyName = property.Name; // 检查必填 if (validate.Required) { if (value == null || string.IsNullOrEmpty(value.ToString())) { errors.Add($"{propertyName} 不能为空"); continue; } } // 检查字符串长度 if (value is string strValue) { if (strValue.Length < validate.MinLength) errors.Add($"{propertyName} 长度不能小于 {validate.MinLength}"); if (validate.MaxLength > 0 && strValue.Length > validate.MaxLength) errors.Add($"{propertyName} 长度不能大于 {validate.MaxLength}"); } // 检查数值范围 if (value is int intValue) { if (intValue < validate.MinLength) errors.Add($"{propertyName} 不能小于 {validate.MinLength}"); if (validate.MaxLength > 0 && intValue > validate.MaxLength) errors.Add($"{propertyName} 不能大于 {validate.MaxLength}"); } } } return errors; } } // 使用 class Program { static void Main() { User user = new User { Name = "A", // 太短 Password = "123", // 太短 Age = 150 // 太大 }; var errors = Validator.Validate(user); if (errors.Count > 0) { Console.WriteLine("验证失败:"); foreach (string error in errors) { Console.WriteLine($" - {error}"); } } else { Console.WriteLine("验证通过"); } } }

第五部分:逐步练习

练习1:文档生成器

csharp

// 创建文档特性 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property)] public class DocumentationAttribute : Attribute { public string Description { get; } public DocumentationAttribute(string description) { Description = description; } } // 标记代码 [Documentation("表示一个用户账户")] public class UserAccount { [Documentation("用户名,必须是唯一的")] public string Username { get; set; } [Documentation("获取用户欢迎信息")] public string GetWelcomeMessage() { return $"欢迎, {Username}!"; } } // TODO: 写一个程序读取这些文档信息,生成API文档

练习2:权限控制

csharp

// 权限特性 public enum UserRole { Guest, User, Admin } [AttributeUsage(AttributeTargets.Method)] public class RequireRoleAttribute : Attribute { public UserRole RequiredRole { get; } public RequireRoleAttribute(UserRole role) { RequiredRole = role; } } // 使用 public class AdminPanel { [RequireRole(UserRole.Admin)] public void DeleteUser(string username) { Console.WriteLine($"删除用户: {username}"); } [RequireRole(UserRole.User)] public void ChangePassword(string newPassword) { Console.WriteLine("修改密码"); } } // TODO: 写一个安全检查器,在执行方法前检查用户权限

第六部分:特性使用技巧和注意事项

6.1 技巧

csharp

// 1. 多个特性可以叠加 [Serializable] [AuthorInfo("张三")] [Todo("添加序列化测试")] public class MyClass { } // 2. 可以缩写,去掉"Attribute"后缀 [AuthorInfo("李四")] // 等同于 [AuthorInfoAttribute("李四")] // 3. 可以放在同一行 [Serializable][AuthorInfo("张三")] public class MyClass { }

6.2 注意事项

  1. 特性只是元数据:它们不会改变代码逻辑,只是添加信息

  2. 需要反射读取:要使用特性信息,需要通过反射

  3. 性能考虑:反射比较慢,不要在频繁调用的地方使用

  4. 编译时特性:有些特性(如Conditional)是给编译器看的

总结对比

用途类比代码示例
标记过时方法贴"过期"标签[Obsolete]
条件编译写"仅调试使用"[Conditional]
序列化控制标"可存档"[Serializable]
添加文档写备注自定义文档特性
权限控制贴"权限等级"自定义角色特性

关键理解

  1. 特性就像代码的标签和备注

  2. 不会直接影响代码运行,但可以通过反射获取这些信息

  3. 内置特性解决常见问题,自定义特性解决特定问题

  4. 特性让代码更加声明式和自描述

从最简单的[Obsolete]开始,逐步理解特性如何工作,然后创建自己的特性来解决实际问题,这是掌握C#特性的最佳路径。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/23 14:52:58

Ubuntu安装GNOME桌面增强PyTorch开发体验

Ubuntu 安装 GNOME 桌面增强 PyTorch 开发体验 在深度学习项目开发中&#xff0c;很多开发者都曾面临这样的困境&#xff1a;好不容易配置好 CUDA 和 PyTorch 环境&#xff0c;却因为缺乏图形界面而无法直观查看训练曲线&#xff1b;想拖拽上传数据集时只能靠 scp 命令反复折腾…

作者头像 李华
网站建设 2026/6/23 14:52:04

gpt-oss-20b能否用于文本摘要任务?实测效果报告

gpt-oss-20b能否用于文本摘要任务&#xff1f;实测效果报告 在信息爆炸的时代&#xff0c;每天产生的文本数据量以TB计——新闻、论文、会议记录、财报、社交媒体内容……如何从海量文字中快速提取核心信息&#xff0c;已成为自然语言处理领域最现实的需求之一。文本摘要作为“…

作者头像 李华
网站建设 2026/6/23 14:53:29

3步搞定!网易云NCM音乐格式转换全攻略

3步搞定&#xff01;网易云NCM音乐格式转换全攻略 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗&#xff1f;ncmdump这款轻量级工具正是你的救星&#xff0c;它能快…

作者头像 李华
网站建设 2026/6/23 13:41:39

Qwen3-VL-30B在金融报表图表解析中的精准度表现测试

Qwen3-VL-30B在金融报表图表解析中的精准度表现测试 在现代金融研究中&#xff0c;分析师每天面对海量的年报、季报和投资简报。这些文档往往包含大量以图表形式呈现的关键数据——收入趋势、利润结构、市场份额变化等。传统方式依赖人工逐页阅读、截图比对、手动录入&#xff…

作者头像 李华
网站建设 2026/6/23 14:49:20

Ruby CGI 编程

Ruby CGI 编程 引言 CGI(Common Gateway Interface)是一种网络服务器与外部应用程序进行交互的标准协议。在Web开发中,CGI技术允许服务器执行客户端提交的脚本,从而实现动态网页的生成。Ruby作为一种灵活、高效的编程语言,与CGI技术结合,可以轻松实现各种Web应用程序的…

作者头像 李华