1、特性
特性是一种允许我们向程序的程序集添加元数据的语言结构,用于保存程序结构信息的某种特殊类型的类
特性提供功能强大的方法以将声明信息与C#代码(类型、方法、属性等)相关联。
特性与程序实体关联后,即可在运行时使用反色和查询特性信息
特性的目的是告诉编译器把程序结构的某组元数据嵌入程序中
它可以放置在几乎所有的声明中(类、变量、函等)
即:特性本质是一个类,可以利用特性类为元数据添加额外信息,之后可以通过反射来获取这些额外信息
2、自定义特性
继承特性基类 Attribute
// 命名规则: 特性意义(例如:MyCustom)+Attribute class MyCustomAttribute : Attribute { // 特性中的成员,一般根据需求来写 public string info; public MyCustomAttribute(string info) { this.info = info; } public void TestFun() { Console.WriteLine("特性的方法"); } }
3、特性的使用
基本语法:[特性名(参数列表)]
本质上就是在调用特性类的构造函数
写在类、函数、变量的上一行,表示它们具有该特性的信息。特性可以有多个,即使相同(看第4点,限制自定义特性的适用范围)。
// 后面的 Attribute 会被系统省略 [MyCustom("自定义特性,MyClass这个类是干嘛的")] class MyClass { [MyCustom("这是一个变量")] public int value; [MyCustom("这是一个干嘛的函数")] public void TestFun([MyCustom("这是一个参数")]int v) { } }
在使用时
MyClass mc = new MyClass(); Type t = typeof(MyClass); // 判断是否使用了某个特性 // 参数一:特性的类型 // 参数二:搜索继承链,从父类中找是否存在这个特性(属性和事件忽略此参数) if (t.IsDefined(typeof(MyCustomAttribute), false)) { Console.WriteLine("该类应用了MyCustom特性"); }
这里只会检测type关联的类是否存在特性,对于类中的成员是否存在特性不进行判断,需要单独获得成员的type
// 得到特性信息 object[] obj = t.GetCustomAttributes(true); for(int i = 0; i < obj.Length; ++i) { if (obj[i] is MyCustomAttribute) { // 打印特性的信息 Console.WriteLine((obj[i] as MyCustomAttribute).info); // 执行特性类中的方法 (obj[i] as MyCustomAttribute).TestFun(); } }
4、限制自定义特性的适用范围
通过为特性类夹特性,限制其适用范围
// 参数一:AttributeTargets —— 特性能用在哪些地方 // 参数二:AllowMultiple —— 是否允许多个特性实例用在同一个目标上 // 参数三:Inherited —— 特性是否能够被派生类和重写成员继承 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true, Inherited =true)] public class MyCustom2Attribute : Attribute { }
5、系统自带特性
(1)过时特性
[Obsolete]
用于提示用户使用的方法等成员已经过时,建议使用新方法
一般加在函数前的特性
class TestClass { // 参数一:调用过时方法时提示的内容 // 参数二:true 使用该方法时会报错; false 使用该方法时警告 [Obsolete("OldFunc方法已经过时,请使用Func方法", false)] public void OldFunc(string str) { } public void Func(string str) { } }
(2)调用者信息特性
需要引用命名空间:using System.Runtime.CompilerServices;
哪个文件调用:[CallerFilePath]
哪一行调用:[CallerLineNumber]
哪个函数调用:[CallerMemberName]
一般作为函数参数特性
例如在某个类中有这样一个函数:
public void SpeakCaller(string str, [CallerFilePath]string fileName = "", [CallerLineNumber]int line = 0, [CallerMemberName]string target = "") { }
那么在后面参数不传入的情况下,会自动补值,但是一定要是缺省参数的格式,否则参数必须要自己传入,特性便失去了意义。
配合try...catch...可以帮助查错
(3)条件编译特性
[Conditional]
它会和预处理指令#define配合使用
需要引用命名空间 using System.Diagnostics;
主要可以用在一些调试代码上,有时想执行有时不想执行的代码
[Conditional("Fun")] static void Fun() { Console.WriteLine("Fun函数"); }
在这里如果没有#define Fun,那么调用Fun()是没有效果的。注意!!define命令必须放在最前面(using前面)
(4)外部Dll包函数特性
需要引用命名空间using System.Runtime.InteropServices;
[DllImport("dll包的文件名/路径")]
用来标记非.Net(C#)的函数,表明该函数在一个外部的dll中定义
一般用来调用C或者C++的dll包写好的方法
[DllImport("Test.dll")] public static extern int Add(int a, int b); // Test.dll包中有这样一个方法