VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > c#编程 >
  • 自定义特性

自定义特性
概述
特性(Attribute)是C#中给代码添加元数据的机制,它能在编译时将额外信息嵌入程序集,运行时通过反射读取这些信息。自定义特性是用户自己定义的特性类,用于标记类、方法、属性等成员,实现声明式编程(比如标记“这个类需要日志记录”“这个方法需要权限验证”)。
特性不是代码逻辑的一部分,而是“标签”,需结合反射才能发挥作vb.net教程C#教程python教程SQL教程access 2010教程用。常见场景:ORM框架的[Table]标记、ASP.NET Core的[Route]路由标记、单元测试的[Test]标记等。

  1. 自定义特性的核心规则
    自定义特性需遵循以下规则:
    1.继承Attribute类:所有特性都必须直接或间接继承System.Attribute;
    2.命名规范:特性类名建议以Attribute结尾(使用时可省略,如[Author]等价于[AuthorAttribute]);
    3.AttributeUsage特性:可选,用于限制特性的应用范围(如只能标记类、方法)、是否允许重复、是否继承。
  2. 实例代码:自定义[Author]特性
    以下实例定义一个AuthorAttribute特性,标记类和方法的作者信息,再通过反射读取这些信息:
    csharp
	using System;
	using System.Reflection;
	
	// -------------------------- 1. 定义自定义特性 --------------------------
	// AttributeUsage:限制特性的应用范围(类、方法),允许重复,不继承
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
	public class AuthorAttribute : Attribute
	{
	// 特性的属性(存储元数据)
	public string Name { get; }
	public string Email { get; set; }
	public DateTime Date { get; }
	
	// 特性的构造函数(必须有公共构造函数)
	public AuthorAttribute(string name, string date)
	{
	Name = name;
	Date = DateTime.Parse(date); // 解析日期字符串
	}
	}
	
	// -------------------------- 2. 应用自定义特性 --------------------------
	// 给User类应用[Author]特性(可省略Attribute后缀)
	[Author("Alice", "2025-01-01", Email = "alice@example.com")]
	public class User
	{
	public int Id { get; set; }
	public string Name { get; set; }
	
	// 给方法应用[Author]特性(允许重复,添加两个作者)
	[Author("Bob", "2025-01-02", Email = "bob@example.com")]
	[Author("Charlie", "2025-01-03")]
	public void UpdateProfile()
	{
	Console.WriteLine("更新用户资料");
	}
	}
	
	// -------------------------- 3. 反射读取特性信息 --------------------------
	class AttributeExample
	{
	static void Main()
	{
	Type userType = typeof(User);
	
	// 读取类上的[Author]特性
	Console.WriteLine("=== User类的作者信息 ===");
	var classAuthors = userType.GetCustomAttributes<AuthorAttribute>(inherit: false);
	foreach (var author in classAuthors)
	{
	Console.WriteLine($"作者:{author.Name},日期:{author.Date:yyyy-MM-dd},邮箱:{author.Email ?? "无"}");
	}
	
	// 读取方法上的[Author]特性
	Console.WriteLine("
=== UpdateProfile方法的作者信息 ===");
	MethodInfo updateMethod = userType.GetMethod("UpdateProfile")!;
	var methodAuthors = updateMethod.GetCustomAttributes<AuthorAttribute>(inherit: false);
	foreach (var author in methodAuthors)
	{
	Console.WriteLine($"作者:{author.Name},日期:{author.Date:yyyy-MM-dd},邮箱:{author.Email ?? "无"}");
	}
	}
	}
  1. 逐行讲解
    3.1 定义特性类
    [AttributeUsage(...)]:
    oAttributeTargets.Class | AttributeTargets.Method:特性只能应用到类和方法上;
    oAllowMultiple = true:允许同一个成员应用多个该特性(如UpdateProfile方法有两个[Author]);
    oInherited = false:子类不会继承父类的该特性;
    public class AuthorAttribute : Attribute:必须继承Attribute,类名以Attribute结尾;
    构造函数:特性的构造函数用于接收必填参数(如name和date),属性用于接收可选参数(如Email)。
    3.2 应用特性
    [Author("Alice", "2025-01-01", Email = "alice@example.com")]:
    o应用特性时,可省略Attribute后缀(Author等价于AuthorAttribute);
    o构造函数参数(name、date)是必填的,属性参数(Email)是可选的,需指定名称。
    3.3 反射读取特性
    userType.GetCustomAttributes(inherit: false):
    o泛型方法GetCustomAttributes直接获取指定类型的特性,避免类型转换;
    oinherit: false:不读取父类继承的特性(与AttributeUsage的Inherited一致);
    遍历特性集合,输出存储的元数据(作者、日期、邮箱)。
  2. 基础知识拓展
    4.1 AttributeUsage的关键参数
    | 参数 | 作用 |
    | ---- | ---- |
    | ValidOn | 指定特性可应用的目标(如Class、Method、Property等,用` |
    | AllowMultiple | 是否允许同一个成员应用多个该特性(默认false) |
    | Inherited | 子类是否继承父类的该特性(默认true,对方法/属性的继承需看成员是否重写) |

示例:限制特性只能应用到属性上:
csharp

	[AttributeUsage(AttributeTargets.Property)]
	public class RequiredAttribute : Attribute {}

4.2 特性的生命周期
特性是编译时嵌入元数据的,运行时通过反射读取。它不会影响代码的执行逻辑,只是提供额外信息。
编译时特性:仅在编译阶段生效(如[Obsolete]标记,编译器会警告);
运行时特性:需通过反射读取(如[Author]特性,运行时获取作者信息)。
4.3 特性与反射的结合场景
ORM框架:用[Table("Users")]标记类对应的数据表,[Column("user_id")]标记属性对应的数据列;
权限验证:用[RequirePermission("Admin")]标记方法,运行时反射检查用户权限;
序列化:用[JsonIgnore]标记不需要序列化的属性(如Newtonsoft.Json)。
4.4 特性的性能
反射读取特性会有轻微性能开销,但特性本身是元数据,读取时无需实例化对象,开销远小于反射调用方法。对于高频场景,可缓存特性信息(如将类的特性存储到静态字典)。
5. 常见问题与解决
5.1 特性不生效
原因:忘记继承Attribute类,或AttributeUsage的ValidOn指定错误;
解决:确保特性类继承Attribute,且ValidOn包含目标成员类型(如方法需包含AttributeTargets.Method)。
5.2 无法读取多个特性
原因:AttributeUsage的AllowMultiple未设为true;
解决:设置AllowMultiple = true,允许同一个成员应用多个特性。
5.3 读取到父类的特性
原因:反射时inherit参数设为true,且AttributeUsage的Inherited为true;
解决:反射时指定inherit: false,或设置AttributeUsage的Inherited = false。
6. 最佳实践
1.命名规范:特性类名以Attribute结尾(使用时可省略);
2.限制应用范围:用AttributeUsage明确特性的应用目标,避免滥用;
3.保持特性简洁:特性只存储元数据,不包含业务逻辑(逻辑应放在反射读取特性的代码中);
4.缓存特性信息:高频读取特性时,缓存结果到静态字典,减少反射开销;
5.避免过度使用:特性是补充信息,不应替代代码逻辑(如不能用特性实现复杂的权限验证,只能标记需要验证)。
总结
自定义特性是C#声明式编程的核心工具,通过“标记+反射”的组合,能灵活地给代码添加元数据。核心要点:
1.自定义特性需继承Attribute类,用AttributeUsage限制应用范围;
2.应用特性时可省略Attribute后缀,构造函数接收必填参数,属性接收可选参数;
3.反射读取特性时用GetCustomAttributes,注意inherit参数的设置;
4.特性适合存储静态元数据,不适合包含业务逻辑。
掌握自定义特性,能让你写出更灵活、可扩展的代码(如插件系统、框架扩展),是进阶C#开发的必备技能。
(本节完)
下一章:LINQ查询语法与方法语法
(深入讲解LINQ的两种语法,以及如何用LINQ操作集合数据)

本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49390.html


相关教程