C# 高级--反射 详解

一、反射是什么

1、C#编译运行过程

高级语言->编译->dll/exe文件->CLR/JIT->机器码

2、原理解析
metadata:元数据数据清单,记录了dll中包含了哪些东西,是一个描述。
IL:中间语言,编译把高级语言编译后得到的C#中最真实的语言状态,面向对象语言。

反射:来自于System.Reflection,是一个帮助类库,可以读取dll/exe中metadata,使用metadata创建对象。

Emit:一种反射技术,可以动态创建dll/exe。

反编译工具:ILSpy可以反编译dll/exe,查看对应的C#/IL代码。

二、反射创建对象

1、动态读取dll

  • LoadFrom:dll全名称,需要后缀
  • LoadFile:全路径,需要dll后缀
  • Load:dll名称不需要后缀
//1、动态读取dll的三种方式
//(1)LoadFrom:dll全名称,需要后缀                        
Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
//(2)LoadFile:全路径,需要dll后缀
//Assembly assembly1 = Assembly.LoadFile(@"dll文件全路径");
//(3)Load:dll名称 不需要后缀
//Assembly assembly2 = Assembly.Load("Business.DB.SqlServer");

2、获取类型

//2、获取某一个具体的类型,参数需要是类的全名称
Type type1 = assembly.GetType("Business.DB.SqlServer.SqlServerHelper");

3、创建对象

  • 直接传类型
  • 重载方法,传dll的全名称
  • 返回值是object类型,不能直接调用方法
//3、创建对象
//(1)直接传类型
object? oInstance = Activator.CreateInstance(type1);
//(2)重载方法,传dll的全名称
//object? oInstanc1= Activator.CreateInstance("Business.DB.SqlServer.dll", "Business.DB.SqlServer.SqlServerHelper");
//a.oInstance.Query();//报错了:因为oInstance当做是一个object类型,object类型是没有Query方法;C#语言是一种强类型语言;编译时决定你是什么类型,以左边为准;不能调用是因为编译器不允许;实际类型一定是SqlServerHelper;
//b.如果使用dynamic 作为类型的声明,在调用的时候,没有限制;
//c.dynamic :动态类型:不是编译时决定类型,避开编译器的检查;运行时决定是什么类型
//d.dynamic dInstance = Activator.CreateInstance(type);
//e.dInstance.Query();
//f.dInstance.Get(); //报错了--因为SqlServerHelper没有Get方法

4、类型转换

//4、类型转换
// SqlServerHelper helper = (SqlServerHelper)oInstance; //不建议这样转换--如果真实类型不一致--会报报错; 
IDBHelper helper = oInstance as IDBHelper;//如果类型一直,就转换,如果不一致;就返回null

三、反射创建对象详解

using System;namespace MyReflecttion
{/// <summary>/// 反射测试类/// </summary>public class ReflectionTest{#region Actor/// <summary>/// 无参构造函数/// </summary>public ReflectionTest(){Console.WriteLine($"这里是{this.GetType()} 无参数构造函数");}/// <summary>/// 带参数构造函数/// </summary>/// <param name="name"></param>public ReflectionTest(string name){Console.WriteLine($"这里是{this.GetType()} 有参数构造函数");}public ReflectionTest(int id){Console.WriteLine($"这里是{this.GetType()} 有参数构造函数");}public ReflectionTest(int id, string name){Console.WriteLine($"这里是{this.GetType()} 有参数构造函数");}public ReflectionTest(string name,int id ){Console.WriteLine($"这里是{this.GetType()} 有参数构造函数");}#endregion#region Method/// <summary>/// 无参方法/// </summary>public void Show1(){Console.WriteLine($"这里是{this.GetType()}的Show1" );}/// <summary>/// 有参数方法/// </summary>/// <param name="id"></param>public void Show2(int id){Console.WriteLine($"这里是{this.GetType()}的Show2");}/// <summary>/// 重载方法之一/// </summary>/// <param name="id"></param>/// <param name="name"></param>public void Show3(int id, string name){Console.WriteLine($"这里是{this.GetType()}的Show3");}/// <summary>/// 重载方法之二/// </summary>/// <param name="name"></param>/// <param name="id"></param>public void Show3(string name, int id){Console.WriteLine($"这里是{this.GetType()}的Show3_2");}/// <summary>/// 重载方法之三/// </summary>/// <param name="id"></param>public void Show3(int id){Console.WriteLine($"这里是{this.GetType()}的Show3_3");}/// <summary>/// 重载方法之四/// </summary>/// <param name="name"></param>public void Show3(string name){Console.WriteLine($"这里是{this.GetType()}的Show3_4");}/// <summary>/// 重载方法之五/// </summary>public void Show3(){Console.WriteLine($"这里是{this.GetType()}的Show3_1");}/// <summary>/// 私有方法/// </summary>/// <param name="name"></param>private void Show4(string name)  //肯定是可以的{Console.WriteLine($"这里是{this.GetType()}的Show4");}/// <summary>/// 静态方法/// </summary>/// <param name="name"></param>public static void Show5(string name){Console.WriteLine($"这里是{typeof(ReflectionTest)}的Show5");}#endregion}
}

泛型方法泛型类

using System;namespace MyReflecttion
{public class GenericMethod{public void Show<T, W, X>(T t, W w, X x){Console.WriteLine($"t.type={t.GetType().Name},w.type={ w.GetType().Name},x.type={x.GetType().Name}");}}public class GenericClass<T, W, X>{public void Show(T t, W w, X x){Console.WriteLine($"t.type={t.GetType().Name},w.type={w.GetType().Name},x.type={x.GetType().Name}");}}public class GenericDouble<T>{public void Show<W, X>(T t, W w, X x){Console.WriteLine($"t.type={t.GetType().Name},w.type={w.GetType().Name},x.type={x.GetType().Name}");}}
}

1、创建对象

(1)调用无参数构造函数的
Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.ReflectionTest");
object noParaObject = Activator.CreateInstance(type);
(2)调用有参数构造函数的

需要传递一个object类型的数组作为参数,参数按照从昨往右严格匹配,如果没有匹配的报异常

Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.ReflectionTest");
object paraObject = Activator.CreateInstance(type, new object[] { 123 });
object paraObject1 = Activator.CreateInstance(type, new object[] { "三三" });
object paraObject2 = Activator.CreateInstance(type, new object[] { 234, "四四" });
object paraObject3 = Activator.CreateInstance(type, new object[] { "五五", 456 });
四、反射调用方法详解

获取方法MethodInfo,执行MethodInfo 的Invoke方法,传递方法所在的类的实例对象+参数

1、调用无参数的方法

Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.ReflectionTest");
object oInstance = Activator.CreateInstance(type);
MethodInfo show1 = type.GetMethod("Show1");
show1.Invoke(oInstance, new object[] { });
show1.Invoke(oInstance, new object[0]);
show1.Invoke(oInstance, null);

2、调用有参数的方法

需要通过方法参数类型类区别方法,传递参数,严格匹配参数类型

Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.ReflectionTest");
object oInstance = Activator.CreateInstance(type);
MethodInfo show2 = type.GetMethod("Show2");
show2.Invoke(oInstance, new object[] { 123 });
MethodInfo show31 = type.GetMethod("Show3", new Type[] { typeof(string), typeof(int) });
show31.Invoke(oInstance, new object[] { "一一一", 234 });
MethodInfo show32 = type.GetMethod("Show3", new Type[] { typeof(int) });
show32.Invoke(oInstance, new object[] { 345 });
MethodInfo show33 = type.GetMethod("Show3", new Type[] { typeof(string) });
show33.Invoke(oInstance, new object[] { "二二二" });
MethodInfo show34 = type.GetMethod("Show3", new Type[0]);
show34.Invoke(oInstance, null);

3、调用私有方法

在获取方法的时候,加上参数BindingFlags.NonPublic | BindingFlags.Instance

Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.ReflectionTest");
object oInstance = Activator.CreateInstance(type);
MethodInfo show4 = type.GetMethod("Show4", BindingFlags.NonPublic | BindingFlags.Instance);
show4.Invoke(oInstance, new object[] { "String" });

4、调用静态方法

不需要创建对象也可以调用

Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.ReflectionTest");
MethodInfo show5 = type.GetMethod("Show5");
show5.Invoke(null, new object[] { "String" });

5、调用普通类的泛型方法

获取到泛型方法后需要先确定类型

Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
Type type = assembly.GetType("MyReflecttion.GenericMethod");
object oInstance = Activator.CreateInstance(type);
MethodInfo show = type.GetMethod("Show");
//获取到泛型方法后需要先确定类型
MethodInfo genericshow = show.MakeGenericMethod(new Type[] { typeof(int), typeof(string), typeof(DateTime) });
genericshow.Invoke(oInstance, new object[] { 123, "三三三", DateTime.Now });

6、调用泛型类的普通方法

Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
//泛型类的类型需要在类后面加占位符
Type type = assembly.GetType("MyReflecttion.GenericClass`3");
//泛型类获取到类型后需要先确定类型
Type generType = type.MakeGenericType(new Type[] { typeof(int), typeof(string), typeof(DateTime) });
object oInstance = Activator.CreateInstance(generType);
MethodInfo show = generType.GetMethod("Show");
show.Invoke(oInstance, new object[] { 123, "四四四", DateTime.Now });

7、调用泛型类的泛型方法

Assembly assembly = Assembly.LoadFrom("MyReflecttion.dll");
//泛型类的类型需要在类后面加占位符
Type type = assembly.GetType("MyReflecttion.GenericDouble`1");
//泛型类获取到类型后需要先确定类型
Type generType = type.MakeGenericType(new Type[] { typeof(int) });
object oInstance = Activator.CreateInstance(generType);
MethodInfo show = generType.GetMethod("Show");
//获取到泛型方法后需要先确定类型
MethodInfo genericMethod = show.MakeGenericMethod(new Type[] { typeof(string), typeof(DateTime) });
genericMethod.Invoke(oInstance, new object[] { 123, "五五五", DateTime.Now });

五、反射操作属性字段

  • 普通方法调用属性字段简单快捷,反射操作麻烦点。
  • 类增加一个字段呢,普通方法调用需要修改代码,重新编译发布,代码不稳定,反射赋值没啥优势,反射取值不需要修改代码,代码就更加稳定。
  • type.GetProperties()获取属性,type.GetFields()获取字段。

1、创建测试类

using System;namespace Business.DB.Model
{/// <summary>/// 实体---属性是不能保存数据,只有字段才能保存数据/// </summary>public class People{public People(){Console.WriteLine("{0}被创建", this.GetType().FullName);}public int Id { get; set; }//带有Get Set 方法的叫做属性public string Name { get; set; }public int Age { get; set; }public string Description;//字段}
}

2、传统方式赋值取值

//传统手艺赋值取值
Console.WriteLine("***********传统手艺赋值取值*************");
People people = new People();
people.Id = 134;
people.Name = "WWWW";
people.Age = 25;
people.Description = "XXXXX";
Console.WriteLine($"people.Id={people.Id}");
Console.WriteLine($"people.Name={people.Name}");
Console.WriteLine($"people.Age={people.Age}");
Console.WriteLine($"people.Description={people.Description}");

3、反射方式赋值取值

//反射方式赋值取值
Console.WriteLine("***********反射方式赋值取值*************");
Type type = typeof(People);
object pObject = Activator.CreateInstance(type);
foreach (var prop in type.GetProperties())
{if (prop.Name.Equals("Id")){prop.SetValue(pObject, 134);}else if (prop.Name.Equals("Name")){prop.SetValue(pObject, "WWWW");}else if (prop.Name.Equals("Age")){prop.SetValue(pObject, 25);}                            
}                        
foreach (var prop in type.GetProperties())
{Console.WriteLine($"people.{prop.Name}={prop.GetValue(pObject)}");
}

4、运行结果

***********传统手艺赋值取值*************
Business.DB.Model.People被创建
people.Id=134
people.Name=WWWW
people.Age=25
people.Description=XXXX
***********反射方式赋值取值*************
Business.DB.Model.People被创建
people.Id=134
people.Name=WWWW
people.Age=25

六、反射的局限/性能问题

1、性能对比代码实现

using Business.DB.Interface;
using Business.DB.SqlServer;
using System;
using System.Diagnostics;
using System.Reflection;namespace MyReflecttion
{public class Monitor{public static void Show(){Console.WriteLine("*******************Monitor*******************");long commonTime = 0;long reflectionTime = 0;{Stopwatch watch = new Stopwatch();watch.Start();for (int i = 0; i < 1000_000; i++) //1000000000{IDBHelper iDBHelper = new SqlServerHelper();iDBHelper.Query();}watch.Stop();commonTime = watch.ElapsedMilliseconds;}{Stopwatch watch = new Stopwatch();watch.Start();//优化代码,加载dll放到循环外面Assembly assembly = Assembly.Load("Business.DB.SqlServer");//1 动态加载Type dbHelperType = assembly.GetType("Business.DB.SqlServer.SqlServerHelper");//2 获取类型for (int i = 0; i < 1000_000; i++){//创建对象+方法调用object oDBHelper = Activator.CreateInstance(dbHelperType);//3 创建对象IDBHelper dbHelper = (IDBHelper)oDBHelper;//4 接口强制转换dbHelper.Query();//5 方法调用}watch.Stop();reflectionTime = watch.ElapsedMilliseconds;}Console.WriteLine($"commonTime={commonTime} reflectionTime={reflectionTime}");}}
}

2、运行结果

  • 测试用例:普通方式循环100000次,创建对象+方法调用:16毫秒
    反射方式循环100000次,加载dll+创建对象+方法调用:6300毫秒
  • 加载dll放到循环外面,创建对象+方法调用放循环里面,泛型方法:57毫秒

3、使用反射的建议

反射确实有性能问题,但是差别没有那么大,在需要的地方可以放心使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/60224.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Web前端】Web API:构建Web应用核心

什么是 API API&#xff08;应用程序编程接口&#xff09;是一组定义了软件组件之间如何交互的规则和协议。它允许一个程序调用另一个程序的功能&#xff0c;而不用了解其内部实现细节。 Web 开发中&#xff0c;API 通常用于实现前端与后端之间的通信。 客户端 JavaScript 中的…

Telegram bot Mini-App开发实践---Telegram简单介绍与初始化小程序获取window.Telegram.WebApp对象并解析

➡️【好看的灵魂千篇一律,有趣的鲲志一百六七!】- 欢迎认识我~~ 作者:鲲志说 (公众号、B站同名,视频号:鲲志说996) 科技博主:极星会 星辉大使 后端研发:java、go、python、TS,前电商、现web3 主理人:COC杭州开发者社区主理人 、周周黑客松杭州主理人、 AI爱好…

VRT: 关于视频修复的模型

VRT: 关于视频修复的模型 1. 视频修复的背景与重要性背景介绍&#xff1a;重要性&#xff1a; 2. VRT的重要性和研究背景VRT的背景&#xff1a;VRT的重要性&#xff1a; 3. 视频修复概述3.1 定义与目标3.2 与单图像修复的区别3.3 对时间信息利用的需求 4. VRT模型详解4.1 整体框…

游戏引擎学习第17天

视频参考:https://www.bilibili.com/video/BV1LPUpYJEXE/ 回顾上一天的内容 1. 整体目标&#xff1a; 处理键盘输入&#xff1a;将键盘输入的处理逻辑从平台特定的代码中分离出来&#xff0c;放入更独立的函数中以便管理。优化消息循环&#xff1a;确保消息循环能够有效处理 …

jmeter常用配置元件介绍总结之配置元件

系列文章目录 1.windows、linux安装jmeter及设置中文显示 2.jmeter常用配置元件介绍总结之安装插件 3.jmeter常用配置元件介绍总结之线程组 4.jmeter常用配置元件介绍总结之函数助手 5.jmeter常用配置元件介绍总结之取样器 6.jmeter常用配置元件介绍总结之jsr223执行pytho…

Java基础知识(五)

文章目录 ObjectObject 类的常见方法有哪些&#xff1f; 和 equals() 的区别hashCode() 有什么用&#xff1f;为什么要有 hashCode&#xff1f;为什么重写 equals() 时必须重写 hashCode() 方法&#xff1f; 参考链接 Object Object 类的常见方法有哪些&#xff1f; Object 类…

【大模型】LLaMA: Open and Efficient Foundation Language Models

链接&#xff1a;https://arxiv.org/pdf/2302.13971 论文&#xff1a;LLaMA: Open and Efficient Foundation Language Models Introduction 规模和效果 7B to 65B&#xff0c;LLaMA-13B 超过 GPT-3 (175B)Motivation 如何最好地缩放特定训练计算预算的数据集和模型大小&…

2024 RISC-V中国峰会 安全相关议题汇总

安全之安全(security)博客目录导读 第四届 RISC-V 中国峰会(RISC-V Summit China 2024)于8月21日至23日在杭州成功举办。此次峰会汇聚了 RISC-V 国际基金会、百余家重点企业及研究机构,约3000人线下参与,并在19日至25日间举办了超过20场同期活动,与全球开发者共同…

Pyhon基础数据结构(列表)【蓝桥杯】

a [1,2,3,4,5] a.reverse() print("a ",a) a.reverse() print("a ",a)# 列表 列表&#xff08;list&#xff09;有由一系列按照特定顺序排序的元素组成 列表是有顺序的&#xff0c;访问任何元素需要通过“下标访问” 所谓“下标”就是指元素在列表从左…

【Visual Studio系列教程】如何在 VS 上编程?

上一篇博客中&#xff0c;我们介绍了《什么是 Visual Studio&#xff1f;》。本文&#xff0c;我们来看第2篇《如何在 VS 上编程&#xff1f;》。阅读本文大约10 分钟。我们会向文件中添加代码&#xff0c;了解 Visual Studio 编写、导航和了解代码的简便方法。 本文假定&…

MySQL更换瀚高语法更换

MySQL更换瀚高语法更换 一、前言二、语句 一、前言 水一篇,mysql更换瀚高之后&#xff0c;一些需要更换的语法介绍 > 二、语句 MySQL瀚高MySQL用法瀚高用法说明ifnull(x,y)coalesce(x,y)相同相同用于检查两个表达式并返回第一个非空表达式。如果第一个表达式不是 NULL&…

论文阅读——Intrusion detection systems using longshort‑term memory (LSTM)

一.基本信息 论文名称&#xff1a;Intrusion detection systems using longshort‑term memory (LSTM) 中文翻译&#xff1a;基于长短期记忆(LSTM)的入侵检测系统 DOI&#xff1a;10.1186/s40537-021-00448-4 作者&#xff1a;FatimaEzzahra Laghrissi1* , Samira Douzi2*, Kha…

大数据挖掘期末复习

大数据挖掘 数据挖掘 数据挖掘定义 技术层面&#xff1a; 数据挖掘就是从大量的、不完全的、有噪声的、模糊的、随机的实际应用数据中&#xff0c;提取隐含在其中、人们事先不知道的、但又潜在有用的信息的过程。 数据准备环节 数据选择 质量分析 数据预处理 数据仓库 …

等精度频率计的设计

目录 主控电路设计 频率测量与计算电路设计 顶层电路设计 功能扩展及应用 频率测量的三种方法 等精度频率计通过控制闸门信号与被测信号同步&#xff0c;消除了直接测频法中的计数误差&#xff0c;因而在被测信号频率范围内测量精度基本上是恒定的。 本节以设计能够测量信号…

Matlab使用深度网络设计器为迁移学习准备网络

迁移学习通过对预训练网络进行微调&#xff0c;使深度学习模型能在少量数据下快速适应新任务&#xff0c;类似于“举一反三”&#xff0c;而不需要从头训练。本文使用matlab自带的深度网络设计器&#xff0c;可以便捷地修改预训练网络进行迁移学习&#xff0c;通过对预训练网络…

基于yolov8、yolov5的电塔缺陷检测识别系统(含UI界面、训练好的模型、Python代码、数据集)

摘要&#xff1a;电塔缺陷检测在电力设备巡检、运行维护和故障预防中起着至关重要的作用&#xff0c;不仅能帮助相关部门实时监测电塔运行状态&#xff0c;还为智能化检测系统提供了可靠的数据支撑。本文介绍了一款基于YOLOv8、YOLOv5等深度学习框架的电塔缺陷检测模型&#xf…

代理商培训新策略:内部知识库的高效运用

在竞争激烈的市场环境中&#xff0c;代理商作为企业与终端消费者之间的关键纽带&#xff0c;其专业能力和服务质量直接影响着企业的市场表现和品牌形象。因此&#xff0c;如何对代理商进行高效、系统的培训&#xff0c;以提升其业务能力和服务水平&#xff0c;成为企业亟需解决…

uniapp 相关的swiper的一些注意事项

先推荐一个一个对标pc端swiper的uniapp版本 zebra-swiper 缺点是自定义分页器不是很好处理 不知道怎么弄 优点:可以进行高度自适应 &#xff08;这个uniapp原生swiper没有 只能动态修改 采用js 或者只有几种固定高度时采用变量修改&#xff09; <swiperref"lifeMiddle…

ARM中ZI-data段和RW-data段

ARM中ZI-data段和RW-data段 1、只定义全局变量&#xff0c;不使用&#xff0c;不占用内存空间2、 定义并初始化全局变量为0 占用ZI-Data区域3、定义并初始化全局变量非0 占用RW-Data区域4、增加的是一个int8的数据为什么&#xff0c;size增加不是15、定义的全局变量为0&#xf…

jmeter--CSV数据文件设置--请求体设置变量

目录 一、示例 1、准备组织列表的TXT文件&#xff0c;如下&#xff1a; 2、添加 CSV数据文件设置 &#xff0c;如下&#xff1a; 3、接口请求体设置变量&#xff0c;如下&#xff1a; 二、CSV数据文件设置 1、CSV Data Set Config 配置选项说明 2、示例 CSV 文件内容 3、…