c# 特性基础
Serializable特性的讲解 为什么要用Serializable特性 英[ˈsɪərɪəlaɪzəbl
Obsolete 英[ˈɒbsəliːt]
HttpPost HttpGet (MVC Api)
AttributeUsage:修饰特性的特性,通过它可以限制特性的使用。
FromBody: 指定操作参数仅来自实体的属性传入 System.Net.Http.HttpRequestMessage 的正文。
加了上述特性就能实现对应的功能,为什么这里厉害?
1.特性是什么
(1)特性定义
特性就是一个类,直接或间接继承自Attribute。
下面自定义一个特性:
namespace MyAttribute { /// <summary> /// 直接继承的特性 /// 特性类一般以Attribute为结尾,所以在使用的时候可以默认省略掉Attribute,比如可以这样使用[Custom] /// </summary> public class CustomAttribute : Attribute { } /// <summary> /// 间接继承的特性 /// CustomAttributeChild这个不是以Attribute为结尾的,所以不可以省略,只能这样用[CustomAttributeChild] /// </summary> public class CustomAttributeChild : CustomAttribute { } }
(2)默认特性是不能够重复修饰的
public class CustomAttribute : Attribute { /// <summary> /// 无参数的构造函数 /// 对应的是 [Custom] /// </summary> public CustomAttribute() { } /// <summary> /// 参数为int类型的构造函数 /// 对应的特性使用是 [Custom(1)] /// </summary> /// <param name="id"></param> public CustomAttribute(int id) { } /// <summary> /// 参数为string类型的构造函数 /// 对应的特性使用是[Custom("字符串")] /// </summary> /// <param name="name"></param> public CustomAttribute(string name) { } }
比如在Student类中使用上面特性:
[Custom] [Custom(1)] [Custom("字符串")] public abstract class Student { public int Id { get; set; } public string Name { get; set; } public abstract void Study(); }
此时编译是不会通过的,因为特性默认情况下是不允许重复修饰的,所以需要做其他处理
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] public class CustomAttribute : Attribute { /// <summary> /// 无参数的构造函数 /// 对应的是 [Custom] /// </summary> public CustomAttribute() { } /// <summary> /// 参数为int类型的构造函数 /// 对应的特性使用是 [Custom(1)] /// </summary> /// <param name="id"></param> public CustomAttribute(int id) { } /// <summary> /// 参数为string类型的构造函数 /// 对应的特性使用是[Custom("字符串")] /// </summary> /// <param name="name"></param> public CustomAttribute(string name) { } }
加上 [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]这个特性就可以解决,表示允许重复修饰,它是修饰特性的特性。
3.特性类中除了写构造函数,还可以写字段,属性,方法。
代码:
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)] public class CustomAttribute : Attribute { /// <summary> /// 无参数的构造函数 /// 对应的是 [Custom] /// </summary> public CustomAttribute() { } /// <summary> /// 参数为int类型的构造函数 /// 对应的特性使用是 [Custom(1)] /// </summary> /// <param name="id"></param> public CustomAttribute(int id) { } /// <summary> /// 参数为string类型的构造函数 /// 对应的特性使用是[Custom("字符串")] /// </summary> /// <param name="name"></param> public CustomAttribute(string name) { } /// <summary> /// 字段Remark /// </summary> public string Remark = null; /// <summary> /// 属性 /// </summary> public string Description { get; set; } public void Show() { Console.WriteLine(nameof(CustomAttribute)); } }
使用的时候有如下形式:
[Custom("字符串", Remark = "12", Description = "测试")] [Custom(Remark = "12", Description = "测试")] [Custom(3, Remark = "12", Description = "测试")]
2.特性有什么用,是如何生效的
特性不止是在类上使用,在字段,属性,方法也都可以使用。
如果我们想要获取特性的信息或者调用特性中的方法,可以通过反射来获取并使用metadata(元数据)。在运行过程中通过反射就能检测到特性,能获取到特性的实例,得到特性中的方法等信息:
调用类:
class Program { static void Main(string[] args) { { Console.WriteLine("StudentVip main start"); Student vip = new StudentVip() { Id = 1, Name = "张三--Vip班级" }; vip.Study(); ManagerCenter.ManageStudent<Student>(vip); Console.WriteLine("StudentVip main end"); } { Console.WriteLine("StudentOpen main start"); Student open = new StudentOpen() { Id = 1, Name = "张三--公开课" }; open.Study(); ManagerCenter.ManageStudent<Student>(open); Console.WriteLine("StudentOpen main end"); } } }
反射获取特性信息:
public class ManagerCenter { public static void ManageStudent<S>(S student) where S : Student { Console.WriteLine($"{ student.Id}_{ student.Name}"); Type type = student.GetType(); //提前判断下传来的类是不是有DescriptionAttribute这个特性 if (type.IsDefined(typeof(DescriptionAttribute), true)) { //存在DescriptionAttribute这个特性,所以获取 DescriptionAttribute attribute = (DescriptionAttribute)type.GetCustomAttributes(typeof(DescriptionAttribute), true)[0]; //通过反射调用特性中的方法 attribute.Show(); } } }
学生类:
[Custom] [Custom(1)] [Custom("字符串")] [Custom("字符串", Remark = "12", Description = "测试")] [Custom(Remark = "12", Description = "测试")] [Custom(3, Remark = "12", Description = "测试")] public abstract class Student { public int Id { get; set; } public string Name { get; set; } public abstract void Study(); } [DescriptionAttribute(Description ="这是公开课学员")] public class StudentOpen : Student { public override void Study( ) { Console.WriteLine("StudentOpen Study"); } } [DescriptionAttribute(Description = "这是VIP学员")] public class StudentVip : Student { public override void Study( ) { Console.WriteLine("StudentVip Study"); } }
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | StudentVip main start StudentVip Study 1_张三--Vip班级 DescriptionAttribute被构造 DescriptionAttribute 这是VIP学员 StudentVip main end StudentOpen main start StudentOpen Study 1_张三--公开课 DescriptionAttribute被构造 DescriptionAttribute 这是公开课学员 StudentOpen main end |
上面是一个使用反射获取特性信息的一个例子,特性本身是没什么用的,必须在运行的过程中使用反射主动发现并使用才会就有价值。特性可以在不破坏封装的前提下增加额外的信息或者操作,和面向切面编程AOP很吻合,
在不破坏封装的前提下增加额外的功能。
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术