C#进阶-特性全知识点总结
前言:
特性就像是给代码贴上的**“标签”或“注释”。但它不仅仅是给程序员看的注释,它还是给编译器或程序本身**看的。通过这些标签,你可以告诉程序:“这个方法已经过时了”或者“这个类在保存到数据库时叫另一个名字
一什么是特性
在C#中,特性是一种元数据,元数据是关于数据的数据。
语法特征:特性通常写在类,方法,属性等代码元素的上方,用方括号[ ]包围
作用:它们不直接改变程序的逻辑,但可以在编译时或运行时被读取,从而影响程序的行为
二为什么要用特性
想象你在网购,每一个商品包裹上都有一个快递单(特性):
易碎标识:快递员看到后会轻轻放下(编译器/运行时处理逻辑)。
重量信息:分拣机根据它分配路线(工具处理逻辑)。
在代码里,我们可以用特性来标记:
这个方法是用来测试的。
这个变量必须是 1 到 100 之间的数字。
这个类可以被转换成 XML 格式。
三常见的内置特性
1.[Obsolete]过时
提示开发者,这个方法已经旧了,建议使用新的
[Obsolete("请使用 NewMethod,旧方法将在下个版本移除")] public void OldMethod() { // ... }如果你在代码里调用它,编译器会显示一条警告
用法1:只提示警告
[Obsolete] public void OldMethod() { }效果:
- 调用这个方法时,VS 会画绿色波浪线
- 只是警告,代码照样编译、照样运行
用法2:带提示文字的警告
[Obsolete("这个方法旧了,请用 NewMethod 代替")] public void OldMethod() { }效果:
- 警告 + 你写的提示文字
- 还是能编译、能运行
用法3:直接报错,不让编译
[Obsolete("已废弃,禁止使用", true)]第二个参数写true:
- 变成错误,红色波浪线
- 无法编译通过,直接禁止使用
2.[Serializable](可序列化)(老板二进制序列化方式,现在不用了)
1.什么是序列化
序列化的本质,就是把内存里的对象(比如C#的类实例),转换成一串可以存储/传输的格式,之后还能完整还原成原来的对象
- 就像把一个复杂的家具,拆解打包成零件(序列化),运到新家再组装回去(反序列化)。
- 不同的「打包规则」,就是不同的序列化方案
2.[Serializable]到底给谁用?
告诉编译器,这个类的内容可以被转换成二进制流存到硬盘或通过网络传输。
它是给.NET 原生二进制序列化用的:
BinaryFormatterSoapFormatter- 旧版 Remoting
- 旧版 AppDomain 跨域传对象
- 一些老的缓存框架
这些东西要把对象打成二进制字节流,就要求类必须标记[Serializable],否则直接报错。
3.[Conditional]
总结
#define DEBUG= 开关(预处理宏)[Conditional("DEBUG")]= 听开关的标签- 开关开 → 方法调用保留
- 开关关 → 方法调用被删除
1.宏是什么
宏 = 代码里的 “代号”,在编译前会被替换成具体内容。
它不是一个真实的变量,也不是一个真实的函数,它只是文本替换
2.什么是预处理宏
核心作用:
给方法加上这个特性,只有在编译时定义了指定的「预处理宏」,这个方法的调用才会被编译器保留;否则调用会被直接删除,方法本身也不会被编译进最终程序。
最经典的用法:Debug 日志只在调试模式(DEBUG宏)下输出日志,Release 模式下自动消失,完全不影响正式包的性能和体积。
3.如何使用
#define DEBUG就是一个开关
这个开关就是预处理宏,存在就是打开,不存在就是关闭
1.[Conditional(DEBUG)]是什么
四自定义特性
1.什么是自定义特性
- 特性 =给类 / 方法 / 属性贴个标签
- 自定义特性 =你自己写一个标签
- 本质就是一个类,必须继承自
Attribute
2.最简单的自定义特性
这个类的作用就是用来装数据的。当你把[MyTag(Note = "测试", Level = 1)]贴在某个方法或类上时,其实就是创建了这个类的一个实例,并把Note和Level的值赋给了这个实例的属性。
// 1. 类名必须以 Attribute 结尾 public class MyTagAttribute : Attribute { // 2. 可以放一些字段/属性 public string Note { get; set; } public int Level { get; set; } }特性不负责执行逻辑,特性只负责 “存数据”,反射负责 “读数据 + 干活”。
使用时可以省略Attribute,直接写:
[MyTag(Note = "测试标签", Level = 1)] public class TestClass { }3.控制特性贴在哪里
用[AttributeUsage]限制这个标签贴在哪里
[AttributeUsage( AttributeTargets.Class | // 能贴类 AttributeTargets.Method | // 能贴方法 AttributeTargets.Property, // 能贴属性 AllowMultiple = true, // 允许贴多次 Inherited = true // 子类继承标签 )] public class MyTagAttribute : Attribute { }4.特性怎么用
特性自己不会干活,必须反射读取它,然后做逻辑
示例
// 贴标签 [MyTag(Note = "旧方法", Level = 2)] public void Test() { }读取
// 获取方法上的特性 var method = typeof(类名).GetMethod("Test"); var attr = method.GetCustomAttribute<MyTagAttribute>(); if (attr != null) { Console.WriteLine(attr.Note); Console.WriteLine(attr.Level); }五特性的语法结构
特性本质上是一个继承自System.Attribute的类。它的使用语法如下:
位置:紧贴在目标代码上方。
参数:有些特性需要传入参数(就像构造函数一样)。
后缀:所有的特性类名都以
Attribute结尾,但在使用时可以省略(如[ObsoleteAttribute]简写为[Obsolete])。
六如何自定义特性
当你发现内置特性不够用时,可以自己写一个。
定义:创建一个类,继承
Attribute。限制:使用
[AttributeUsage]限制该特性可以贴在什么地方(只能贴在类上?还是只能贴在方法上?)。
// 1. 定义特性 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class DeveloperInfoAttribute : Attribute { public string Name { get; set; } public DeveloperInfoAttribute(string name) { this.Name = name; } } // 2. 使用特性 [DeveloperInfo("小明")] public class MyProgram { }七特性是如何生效的
这是一个关键点:特性贴上去后,如果不去读取它,它就是一张死纸。在 C# 中,我们使用一种叫反射 (Reflection)的技术,在程序运行的时候去“扫描”代码上的标签并做出反应。
编译期:特性被编译进 DLL/EXE 文件的元数据中。
运行期:程序通过反射找到这些标签,根据标签内容执行不同的代码。
