今日关注: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#高级编程--反射与特性
- 2023江苏苏州市昆山市高层次人才选聘进入面试人员名单公告_天天快消息
- 美国一男子因参与“国会山骚乱”事件被判14年监禁
- 83位罗马名宿降临奥林匹克球场,穆帅、国米压力陡增,罗马输不得 世界视讯
- 天天观察:广州至重庆武隆直飞航班将于5月10日开通
- 前沿热点:俄罗斯称美国在乌克兰研究可引发人类疫情的病原体
- 美国纽约州检察长提出新立法,寻求有更大权力监管加密货币行业
- 开源证券:给予国联水产增持评级|快看
- 美国银行业危机持续发酵 又一家美国银行要被关闭?
- 今日大话点卡充值(大话怎么查询所冲过的点卡信息)
- 中国足彩网竞彩6日推荐:罗森博格取胜止颓 全球热议
- 当前播报:5月6日永晶科技氢氟酸价格暂稳
- 焦点观察:光遇5.6任务怎么完成 2023年5月6日每日任务攻略[多图]
- 世界信息:泰迪狗狗多大开始训练_狗狗多大开始训练
- 美媒再批美国政府撤走外交官抛下平民
- 焦点报道:中文传媒(600373)4月6日主力资金净卖出4599.15万元
- 乙肝怎么传染_女被上是什么感觉 每日视点
- 当前关注:漯河:可办理“商转公”业务的银行共有4家
- 今热点:梦见很多鱼密密麻麻_梦见很多鱼
- 卡尔维诺的摘抄_卡尔维诺关于城市的名言 环球最资讯
- 史记中的小故事50字_史记中的小故事简介 通讯
- 全球快报:山西晋城警方破获26年前致一死一重伤抢劫杀人案
- 和嘉控股(00704)收到信达香港发出的催告函 要求偿还未债还本金及应计利息合共2.46亿元
- 每日讯息!合金装备5幻痛存档位置