世界最资讯丨C#高级编程--反射与特性
C#高级编程--反射与特性
(相关资料图)
特性attribute,特性是一种允许我们向程序集添加元数据的语言结构。特性是用于保存程序结构信息的特殊的类;
目标target,应用/添加了特性的程序结构(program construct)叫做目标;
消费者consumer,用来获取和使用元数据/特性的程序叫做特性的消费者;
.NET有很多内置特性,也可以自定义特性;将特性用于描述程序结构;
编译器获取源代码,并从特性产生关于源代码的描述信息(元数据),然后将元数据放到程序集中;
消费者程序读取特性/元数据,编译器既生成特性,同时也消费特性;
Type对象:
对于程序中用到的每一个类型,CLR都会创建一个包含这个类型的信息的Type对象;
不管该类型创建多少个实例,都只有一个Type对象,并且同时关联到该类及该类的所有实例;
获取Type对象的三种方法
分类 | 静态方法 | 关键字 | 实例方法 |
方法 | Type.GetType() | typeof() | object.GetType() |
参数 | string className | Class | 无参 |
示例 | Type t1 = Type.GetType("Person"); | Type t2 = typeof(Person); | Person p = new Person(); Type t3 = p.GetType(); |
错误示例 | //t4 = Type.GetType(Person); //错误:静态方法不能传入类名 //t4 = Type.GetType(p); //错误:静态方法不能传入类对象 | //t4 = typeof("Person"); //错误:不能传入类名 字符串 //t4 = typeof(p); //错误:不能传入类对象 |
//获取type对象的三种方法
//1、Type类的静态方法,只能传入类名字符串作为参数
Type t1 = Type.GetType("Person");
//2、使用typeof关键字,只能传入类名作为参数,而不能传入字符串或者类对象;
Type t2 = typeof(Person);
//3、使用object的实例方法,实例化的对象,调用基类object的GetType方法,无需参数;
Person p = new Person();
Type t3 = p.GetType();
//错误示例
Type t4;
//t4 = Type.GetType(Person); //错误:静态方法不能传入类名
//t4 = Type.GetType(p);//错误:静态方法不能传入类对象
//t4 = typeof("Person");//错误:typeof不能传入类名字符串
//t4 = typeof(p);//错误:typeof不能传入类对象
元数据metadata,有关程序及其类型的数据被称为元数据,它们保存在程序的程序集中;
反射reflection,程序在运行时,可以查看其他程序集或自身程序集的元数据。运行中程序查看元数据的行为叫做反射;
特性目标
All | 所有 | 可以对任何应用程序元素应用属性 |
Constructor | 构造函数 | 可以对构造函数应用属性 |
Method | 方法/函数 | 可以对方法应用属性 |
Property | 属性 | 可以对属性 (Property) 应用属性 (Attribute)。 |
Field | 字段 | 可以对字段应用属性 |
Parameter | 参数 | 可以对参数应用属性。 |
GenericParameter | 泛型参数 | 可以对泛型参数应用属性。 目前,此属性仅可应用于 C#、Microsoft 中间语言 (MSIL) 和已发出的代码中。 |
ReturnValue | 返回值 | 可以对返回值应用属性 |
Delegate | 委托 | 可以对委托应用属性 |
Event | 事件 | 可以对事件应用属性 |
Class | 类 | 可以对类应用属性 |
Struct | 结构体 | 可以对结构应用属性,即值类型。 |
Interface | 接口 | 可以对接口应用属性 |
Enum | 枚举 | 可以对枚举应用属性 |
Assembly | 程序集 | 可以对程序集应用属性 [assembly:MyAttribute(Parameters)] |
Module | 模块 | 可以对模块应用属性。Module引用的是可移植可执行文件(.dll 或 .exe),而不是 Visual Basic 标准模块。 [module:MyAttribute(Parameters)] |
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field),
AllowMultiple=false,Inherited=false]
public class FieldNameAtrribute:Attribute{
private string _fieldName;
public FieldNameAtrribute(string fieldName){_fieldName=fieldName;}
}
特性的特点:
- 特性可以用到那些程序元素上(特性目标AttributeTargets);
- 特性是否可以多次使用(AllowMultiple);
- 特性用到基类上或者接口上时,是否允许子类继承(Inherited);
- 特性需要那些参数(包括可选参数和必选参数);
特性分类(按用途):
- 用于编译器的特性(影响编译过程,如条件编译特性Conditional,过期标记特性Obsolete);
- 用于特性类的特性(指定自定义特性类的一些特点,如指定自定义特性用途的特性AttributeUsage);
- 用于标记元数据的自定义特性(为程序元素添加描述信息的元数据);
消费(查找使用)特性的本质:反射从程序集中读取元数据(特性),并实例化他们所表示的特性类。
特性分类(按程序元素)
- 全局特性,应用于程序集,必须显示指定;
- 局部特性,应用于其他程序元素(可以隐式或显示指定目标元素);
使用特性时的注意事项:
- 使用特性时,可以省略特性类名后的Attribute,当然也可以不省略,二者等价;
- 使用特性时,小括号中的参数有两类:位置参数和命名参数;
- 位置参数是指该特性类的构造函数中的参数;
- 命名参数并不是构造函数中的参数,而是该特性类中的公共字段/属性(这一点与普通类不同);
示例代码
#define condition1#undef condition1using System;using System.Data;using System.Diagnostics;using System.IO;using System.Linq;using System.Reflection;using System.Runtime.InteropServices;namespace ConsoleCore3{ class Program { static void Main(string[] args) { AttributeTest(); } static void AttributeTest() { Person p = new Person("tom"); //通过Person对象获取该类的Type对象 Type t = p.GetType(); //判断该程序元素上是否定义(添加)了某个特性 bool isDefined = t.IsDefined(typeof(SerializableAttribute)); Console.WriteLine($"SerializableAttribute defined:{isDefined}");//True //通过属性名获取属性的元数据(描述信息) PropertyInfo pInfo_Name = t.GetProperty("Name"); //通过反射获取属性类型 bool isString = pInfo_Name.PropertyType == typeof(String); Console.WriteLine($"pName is string:{isString}"); //通过反射获取属性值 Console.WriteLine($"Name={pInfo_Name.GetValue(p)}"); //通过反射为属性赋值 pInfo_Name.SetValue(p, "王英"); Console.WriteLine($"Name={p.Name}"); //通过反射获取属性上是否添加(定义)了某个特性 isDefined = pInfo_Name.IsDefined(typeof(FieldNameAttribute)); Console.WriteLine($"FieldNameAttribute defined:{isDefined}");//True //获取字段上添加的特性对象(元数据) FieldNameAttribute attr = pInfo_Name.GetCustomAttribute(typeof(FieldNameAttribute)) as FieldNameAttribute; //从读取的特性中获取元数据 Console.WriteLine($"FieldName={attr.FieldName},Comment={attr.Comment}"); //通过反射获取某个方法 MethodInfo mInfo_SayHi = t.GetMethod("SayHi"); //通过反射调用方法 mInfo_SayHi.Invoke(p, null); //通过反射获取某个方法--有参数 MethodInfo mInfo_Eat = t.GetMethod("Eat"); //调用有参方法 mInfo_Eat.Invoke(p, new object[] { "馒头" }); //输出结果 //SerializableAttribute defined:True //pName is string:True //Name = tom //Name = 王英 //FieldNameAttribute defined:True //FieldName = 姓名, Comment = 这是姓名属性 //hello world //馒头 真好吃 } static void UnsafeTest() { unsafe { // 错误 CS0208 无法获取托管类型(“Person”)的地址和大小,或者声明指向它的指针 //int size = sizeof(Person); int size = sizeof(double);//8 Console.WriteLine(size); Student s = new Student(); //错误 CS0208 无法获取托管类型(“Program.Student”)的地址和大小,或者声明指向它的指针 //Student* sp = & s; } var p = new Person(); //CS0233“Person”没有预定义的大小,因此 sizeof 只能在不安全的上下文中使用 // int size = sizeof(Person) } unsafe struct Student { string name; int age; } static void AssemblyTest2() { const string className = "mynamespace.Calculator"; Assembly ass = Assembly.LoadFrom(@"D:\VSFile2019\ConsoleCore1\calculator\bin\Debug\netcoreapp3.1\calculator.dll"); //使用Invoke调用函数 //CreateInstance的参数string typeName,必须是 命名空间.类名 的格式; //typeName参数区分大小写 object cal = ass.CreateInstance(className); Console.WriteLine(cal); //calculator.Calculator //GetMethod的参数name区分大小写; //Invoke第一个参数是类的实例化对象,第二个参数是所调用的函数的参数列表,以object数组形式传递 //object[]中的元素个数必须和函数所需参数个数完全一致,否则报错; //object[]中的元素类型必须与参数与类型一致,或者能够隐式转换为目标类型,否则报错; object res = cal.GetType().GetMethod("Add").Invoke(cal, new object[] { 1.5, "a" }); Console.WriteLine(res);//3.5 //使用动态类型调用函数 dynamic calculator = ass.CreateInstance(className); //动态类型没有智能提示,容易出现拼写错误 dynamic result = calculator.Add(2.2, 3.0); Console.WriteLine(result);//5.2 //调用一个不存在的函数,编译器不会报错,只有等到运行时才会出现异常; //:“"mynamespace.Calculator" does not contain a definition for "Pow"” //calculator.Pow(2, 3); //大小写拼写错误,编译器不会有智能提示,只有等到运行时才会出现异常; //:“"mynamespace.Calculator" does not contain a definition for "add"” calculator.add(5, 6); } static void AssemblyTest() { //通过dll名称 获取程序集 Assembly ass = Assembly.Load("itextsharp"); //itextsharp, Version=5.5.7.0, Culture=neutral, PublicKeyToken=8354ae6d2174ddca Console.WriteLine(ass); // Console.WriteLine("types--------------"); // ass.GetTypes().ToList().ForEach (c => Console.WriteLine(c)) ; Assembly ass2 = Assembly.LoadFile(@"D:\VSFile2019\仲裁文档标准化项目V1.0\AxInterop.DSOFramer.dll"); //AxInterop.DSOFramer, Version=2.2.0.0, Culture=neutral, PublicKeyToken=null Console.WriteLine(ass2); ass2.GetTypes().ToList().ForEach(c => Console.WriteLine(c)); Console.ReadLine(); } static void TypeTest2() { Type t = typeof(Person); Console.WriteLine(t.BaseType); //System.Object t.GetConstructors().ToList().ForEach(c => Console.WriteLine(c));//Void .ctor() Console.WriteLine("properties--------------"); t.GetProperties().ToList().ForEach(c => Console.WriteLine(c)); Console.WriteLine("fields--------------"); t.GetFields().ToList().ForEach(c => Console.WriteLine(c)); Console.WriteLine("members--------------"); t.GetMembers().ToList().ForEach(c => Console.WriteLine(c)); //System.Object //Void.ctor() //properties-------------- //Int32 Age //System.String Name //fields-------------- //Int32 id //members-------------- //Int32 get_Age() //Void set_Age(Int32) //System.String get_Name() //Void set_Name(System.String) //Void SayHi() //System.Type GetType() //System.String ToString() //Boolean Equals(System.Object) //Int32 GetHashCode() //Void.ctor() //Int32 Age //System.String Name //Int32 id } static void TestType() { //获取type对象的三种方法 //1、Type类的静态方法,只能传入类名字符串作为参数 Type t1 = Type.GetType("Person"); //2、使用typeof关键字,只能传入类名作为参数,而不能传入字符串或者类对象; Type t2 = typeof(Person); //3、使用object的实例方法,实例化的对象,调用基类object的GetType方法,无需参数; Person p = new Person(); Type t3 = p.GetType(); //错误示例 Type t4; //t4 = Type.GetType(Person); //错误:静态方法不能传入类名 //t4 = Type.GetType(p);//错误:静态方法不能传入类对象 //t4 = typeof("Person");//错误:typeof不能传入类名字符串 //t4 = typeof(p);//错误:typeof不能传入类对象 } //条件编译特性,该特性使用者为编译器 //因此只需在对应方法上标记该特性,而无需手动通过反射获取该特性 //如果定义了宏 condition1,则编译该方法,否则编译器忽略该方法 [Conditional("condition1")] static void TraceMessage(string s) { Console.WriteLine(s); } static void ConditionalTest() { TraceMessage("开始");//如果定义了宏#define condition1,编译器就编译本行代码,否则就忽略 Console.WriteLine("Hello World!"); TraceMessage("结束");//如果定义了宏#define condition1,编译器就编译本行代码,否则就忽略 } } [Serializable] class Person { // [FieldName("Age")] 该特性只能在属性上使用,而不能在字段上使用AttributeTargets.Property //如果想要在多种程序元素上使用,可以使用按位或运算符,连接各种目标元素 //AttributeTargets multiTargets = AttributeTargets.Field|AttributeTargets.Property|AttributeTargets.ReturnValue; public Person() { } public Person(string name) { Name = name; } public int id; [FieldName("Age")] // [FieldName("Age")] 该特性不能在同一个程序元素上重复使用 AllowMultiple = false public int Age { get; set; } [FieldName("姓名", Comment = "这是姓名属性")] public string Name { get; set; } public void SayHi() { Console.WriteLine("hello world"); } public void Eat(string food) { Console.WriteLine($"{food} 真好吃"); } } //用于描述特性用途的特性 // AttributeTargets特性目标,描述该特性可以用于哪些程序元素 //AllowMultiple 是否允许在同一个程序元素上添加多次 //Inherited 是否允许子类继承该特性 [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)] public class FieldNameAttribute : Attribute { public string Comment { get; set; } private string _fieldName; public FieldNameAttribute(string fieldName) { _fieldName = fieldName; } public string FieldName { get => _fieldName; set => _fieldName = value; } }}
标签:
- 世界最资讯丨C#高级编程--反射与特性
- 怀化市沅陵县鹤鸣山小学:骨干展风采 师生同成长
- 小 S 大赞韩国部队锅味道:就跟我的姐夫一样
- 哈登对阵绿军G1两分球命中率62.5% 其余6场季后赛49投12中 全球百事通
- 只有才造句一年级下册_只有才造句一年级
- 科大讯飞宣布推出讯飞星火认知大模型,支持多风格多任务长文本生成
- 速溶咖啡是怎么一步步变成“抖音最火减肥药”的?
- 理想空间--2010上海世博会规划同济作品_关于理想空间--2010上海世博会规划同济作品介绍 焦点快报
- 泰山景点介绍及特点是什么(泰山景点介绍)
- 今日讯!新化县琅塘镇“‘琅塘星’帮代办” 让政务服务便民体系向基层延伸
- 头条焦点:奇瑞集团:4月销售汽车12.7万辆,同比增长128%。
- 【环球聚看点】SHEIN推出平台模式,允许第三方卖家入驻
- 当前焦点!“五一”假期出境游热情恢复 湖南口岸出入境人员近万人次
- 香港,为什么突然出手抢人?-全球快资讯
- 大牌时装秀,频繁暴露隐私部位,到底是追求潮流还是为了博眼球
- 立夏将至农事忙
- 车辆可靠性及耐久性技术研讨会日程安排 当前资讯
- 奥特维 在手订单充足 平台化布局未来可期_天天快讯
- 每日热讯!淮海战役支前工作计划(合集4篇)
- 香港金管局余伟文:银行危机未外溢至亚洲因区内流动性及缓冲强劲|环球播资讯
- 市领导调研农村闲置学校老幼医融合试点工作
- 世界观焦点:股票行情快报:瑞晨环保(301273)5月5日主力资金净卖出9.35万元
- 每日消息!致敬劳动者 | 孔颖:精益求精的降本增效能手
- 防盗门锁芯怎么换_防盗门锁|天天微头条
- 龙山县猛西村给长沙市岳麓区山塘社区写了封感谢信
- 环球快看:盐城这个“圈”,越来越大!
- 鲁南论坛枣庄台儿庄涧头村_鲁南论坛枣庄-当前要闻
- 全球看热讯:tjlnrzs wwwtjlnrzscn
- 新鲜的螃蟹怎么保存多几天 怎样保存螃蟹|世界最新
- 火山的女儿秋火年10月谈心事件选择有什么区别 全球速看料