C# 高级--反射 详解

news2024/11/21 21:07:17

一、反射是什么

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.coloradmin.cn/o/2244881.html

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

相关文章

OpenCV与AI深度学习|16个含源码和数据集的计算机视觉实战项目(建议收藏!)

本文来源公众号“OpenCV与AI深度学习”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;分享&#xff5c;16个含源码和数据集的计算机视觉实战项目 本文将分享16个含源码和数据集的计算机视觉实战项目。具体包括&#xff1a; 1. 人…

【软考网工笔记】网络基础理论——应用层

TLv 基本编码规则&#xff08;BER&#xff09;将ASN.1表示的抽象类型值编码为字节串&#xff0c;这种字节串的结构为&#xff1a;类型——长度——值&#xff0c;简称TLv。 其中&#xff0c;值部分还可以递归的在编码为TLv结构&#xff0c;一具有表达复杂结构的能力。 IP地址…

用Python爬虫“偷窥”1688商品详情:一场数据的奇妙冒险

引言&#xff1a;数据的宝藏 在这个信息爆炸的时代&#xff0c;数据就像是一座座等待挖掘的宝藏。而对于我们这些电商界的探险家来说&#xff0c;1688上的商品详情就是那些闪闪发光的金子。今天&#xff0c;我们将化身为数据的海盗&#xff0c;用Python这把锋利的剑&#xff0…

企业网络安全规划建设实践

规划是指较全面或长远的计划。凡事预则立&#xff0c;不预则废&#xff01; 在企业战略规划方面&#xff0c;随着市场环境变化速度的不断加快&#xff0c;人们越来越意识到企业战略规划对企业生存和发展的重要性&#xff0c;战略规划能帮助企业解决影响组织未来发展最重要、最…

QT基本绘图

QT绘图 1.概述 这篇文章介绍如何绘图 2.绘图基本操作 创建一个普通的widget类型的项目 在widget.h 文件中重写绘图事件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : p…

Linux驱动开发(7):使用设备树实现RGB 灯驱动

通过上一小节的学习&#xff0c;我们已经能够编写简单的设备树节点&#xff0c;并且使用常用的of函数从设备树中获取我们想要的节点资源。 这一小节我们带领大家使用设备树编写一个简单的RGB灯驱动程序&#xff0c;加深对设备树的理解。 1. 实验说明 本节实验使用到 EBF6ULL-…

MATLAB实现GARCH(广义自回归条件异方差)模型计算VaR(Value at Risk)

MATLAB实现GARCH(广义自回归条件异方差)模型计算VaR(Value at Risk) 1.计算模型介绍 使用GARCH&#xff08;广义自回归条件异方差&#xff09;模型计算VaR&#xff08;风险价值&#xff09;时&#xff0c;方差法是一个常用的方法。GARCH模型能够捕捉到金融时间序列数据中的波…

Neo4j下载及其Cypher语法介绍

1.部署安装 Neo4j支持众多平台的部署安装&#xff0c;如&#xff1a;Windows、Mac、Linux等系统。Neo4j是基于Java平台的&#xff0c;所以部署安装前先保证已经安装了Java虚拟机。 在神领物流项目中&#xff0c;我们采用docker的方式进行安装。安装命令如下&#xff1a; dock…

【Redis】实现点赞功能

一、实现笔记点赞 使用redis实现点赞功能&#xff0c;对于一个笔记来说&#xff0c;不同用户只能是点赞和没点赞&#xff0c;点赞过的笔记再点击就应该取消点赞&#xff0c;所以实际上根据需求&#xff0c;我们只需要将点赞的数据存到对应的笔记里&#xff0c;查看对应的笔记相…

开源TTS语音克隆神器GPT-SoVITS_V2版本地整合包部署与远程使用生成音频

文章目录 前言1.GPT-SoVITS V2下载2.本地运行GPT-SoVITS V23.简单使用演示4.安装内网穿透工具4.1 创建远程连接公网地址 5. 固定远程访问公网地址 前言 本文主要介绍如何在Windows系统电脑使用整合包一键部署开源TTS语音克隆神器GPT-SoVITS&#xff0c;并结合cpolar内网穿透工…

【Pytorch】torch.utils.data模块

torch.utils.data模块主要用于进行数据集处理&#xff0c;是常用的一个包。在构建数据集的过程中经常会用到。要使用data函数必须先导入&#xff1a; from torch.utils import data 下面介绍几个经常使用到的类。 torch.utils.data.DataLoader DataLoader(dataset, batch_…

XGBOOST、LightGBM、CATBoost

本文介绍几种不同的 GBDT 优化算法&#xff1a; XGBoost XGBoost 对损失函数展开二阶导&#xff0c;使得提升树能逼近真是损失&#xff0c;增加正则项防止过拟合&#xff0c;XGBoost 公式&#xff1a; L( y i y_i yi​, y ^ i \hat{y}_i y^​i​): 损失函数 Ω ( f k ) \Ome…

论文阅读 SimpleNet: A Simple Network for Image Anomaly Detection and Localization

SimpleNet: A Simple Network for Image Anomaly Detection and Localization 摘要&#xff1a; 该论文提出了一个简单且应用友好的网络&#xff08;称为 SimpleNet&#xff09;来检测和定位异常。SimpleNet 由四个组件组成&#xff1a;&#xff08;1&#xff09;一个预先训练的…

多线程4:线程池、并发、并行、综合案例-抢红包游戏

欢迎来到“雪碧聊技术”CSDN博客&#xff01; 在这里&#xff0c;您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者&#xff0c;还是具有一定经验的开发者&#xff0c;相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导&#xff0c;我将…

Java数据库连接(Java Database Connectivity,JDBC)

1.JDBC介绍 Java数据库连接&#xff08;Java Database Connectivity&#xff0c;JDBC&#xff09;是SUN公司为了简化、统一对数据库的操作&#xff0c;定义的一套Java操作数据库的规范&#xff08;接口&#xff09;。这套接口由数据库厂商去实现&#xff0c;这样&#xff0c;开…

高亮变色显示文本中的关键字

效果 第一步&#xff1a;按如下所示代码创建一个用来高亮显示文本的工具类&#xff1a; public class KeywordUtil {/*** 单个关键字高亮变色* param color 变化的色值* param text 文字* param keyword 文字中的关键字* return*/public static SpannableString highLigh…

2024强化学习的结构化剪枝模型RL-Pruner原理及实践

[2024] RL-Pruner: Structured Pruning Using Reinforcement Learning for CNN Compression and Acceleration 目录 [2024] RL-Pruner: Structured Pruning Using Reinforcement Learning for CNN Compression and Acceleration一、论文说明二、原理三、实验与分析1、环境配置在…

电脑超频是什么意思?超频的好处和坏处

嗨&#xff0c;亲爱的小伙伴&#xff01;你是否曾经听说过电脑超频&#xff1f;在电脑爱好者的圈子里&#xff0c;这个词似乎非常熟悉&#xff0c;但对很多普通用户来说&#xff0c;它可能还是一个神秘而陌生的存在。 今天&#xff0c;我将带你揭开超频的神秘面纱&#xff0c;…

uniapp: vite配置rollup-plugin-visualizer进行小程序依赖可视化分析减少vender.js大小

一、前言 在之前文章《uniapp: 微信小程序包体积超过2M的优化方法&#xff08;主包从2.7M优化到1.5M以内&#xff09;》中&#xff0c;提到了6种优化小程序包体积的方法&#xff0c;但并没有涉及如何分析common/vender.js这个文件的优化&#xff0c;而这个文件的大小通常情况下…

SQL Server Management Studio 的JDBC驱动程序和IDEA 连接

一、数据库准备 &#xff08;一&#xff09;启用 TCP/IP 协议 操作入口 首先&#xff0c;我们要找到 SQL Server 配置管理器&#xff0c;操作路径为&#xff1a;通过 “此电脑” 右键选择 “管理”&#xff0c;在弹出的 “计算机管理” 窗口中&#xff0c;找到 “服务和应用程…