C#基础--反射

反射

一、为什么学习反射

因为反射真的是无处不在,ORM、MVC、IOC、AOP、Attribute等等都会使用到反射。反射是程序员的快乐

二、什么是反射

image-20220302185357180

Ilspy:逆向工程,可以吧DLL/Exe文件反编译回来

image-20220302190153414

DLL/EXE 文件下包含Metadata和IL,IL是对标于C#代码的代码,属于中间语言,是标准的面向对象语言

image-20220302190125450

而Metadata(元数据)是一个清单数据,只是描述了类中有什么,而不是展示所有的实现。一般我们用F12查看元数据,可以发现只有方法体没有声明:

image-20220302190537206

反射是一种工具,命名空间为System.Reflection,可以读取metadata,并使用metadata。(是微软提供的一个帮助类库)

三、Type类

3.1 简介

​ BCL声明了一个叫做Type的抽象类,它被设计用来包含类型的特性,使用这个类的对象能让我们获取程序使用的类型的信息。

​ 由于Type是抽象类,因此它不能有实例,而是在运行时CLR创建从Type(RuntimeType)派生的类的实例。Type包含了类型信息,当我们要访问这些实例时,CLR不会返回派生类的引用而是返回Type基类的引用。为了简单起见,下面篇幅中我会把引用所指向的对象称为Type类型的对象。

Type类的重要事项:

​ ● 对于程序中用到的每一个类型,CLR都会创建一个包含这个类型信息的Type类型的对象

​ ● 程序中用到的每一个类型都会关联到独立的Type类型的对象

​ ● 不管创建的类型有多少个实例,只有一个Type对象会关联到所有这些实例

​ 如果你对C#中有哪些类型不是很了解,那么知识扩展: C#中的类型有:预定义的类型(int,long和string等),BCL中的类型(Console,IEnumerable等)以及用户自定义的类型(MyClass,MyDel等)

3.2 Type类的部分常见成员

成员成员类型描述
Name属性返回类型的名字
FullName属性返回数据类型的完全限定名(包括命名空间名)
NameSpace属性返回包含数据类型声明的命名空间
Assembly属性返回声明类型的程序集。如果类型是泛型的,返回定义这个类型的程序集
GetConstructor(), GetConstructors()方法返回ConstructorInfo类型,用于取得该类的构造函数的信息
GetEvent(), GetEvents()方法返回EventInfo类型,用于取得该类的事件的信息
GetField(), GetFields()方法返回FieldInfo类型,用于取得该类的字段(成员变量)的信息
GetInterface(), GetInterfaces()方法返回InterfaceInfo类型,用于取得该类实现的接口的信息
GetMember(), GetMembers()方法返回MemberInfo类型,用于取得该类的所有成员的信息
GetMethod(), GetMethods()方法返回MethodInfo类型,用于取得该类的方法的信息
GetProperty(), GetProperties()方法返回PropertyInfo类型,用于取得该类的属性的信息

3.3 获取Type对象

都是获取类的引用的数据类型 System.Type

  • GetType()方法继承自Object,所以C#中任何对象都具有GetType()方法,x.GetType(),其中x为变量名

  • typeof(x)中的x,必须是具体的类名、类型名称等,不可以是变量名称

  • System.Type.GetType(),有两个重载方法:

    • 比如有这样一个变量i,Int16 i = new Int16();使用GetType(),i.GetType()返回值是Int16的类型;但是无法使用typeof(i),因为i是一个变量
    • 使用typeof(),则只能:typeof(Int32),返回的同样是Int16的类型
    • 枚举类的转换,String——枚举类,以串口中Parity为例。objParity= (Parity)Enum.Parse(typeof(Parity), “9600”);

获取类的Type属性,除了上面的使用实例获取,或者typeof,还有一种提供类的完整信息字符串,根据类名来获取Type:

//取得当前方法命名空间
str += "命名空间名:" + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Namespace + "\n";
//取得当前方法类全名 包括命名空间
str += "类名:" + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "\n";
//取得当前方法名
str += "方法名:" + System.Reflection.MethodBase.GetCurrentMethod().Name + "\n"; str += "\n";
//取得当前方法类全名 包括命名空间
string classname = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName; //得到的是 命名空间.类
Type type = Type.GetType(classname); //通过类名获取同名类

object obj = System.Activator.CreateInstance(type); //创建实例

四、反射基本操作

4.1 反射–动态加载dll

  1. 通过dll文件名称进行加载

    Assembly assembly = Assembly.Load("DB.SqlServer");// dll名称  默认到当前目录下查找
    
  2. 全名称= 全路径+dll名称 + 后缀

    Assembly assembly = Assembly.LoadFile(@"D:\软谋教育\Ruanmou\Advanced13Encrypt\20191010Advanced13Course3Reflection\20191010Advanced13Course3Reflection\MyReflection\MyReflection\bin\Debug\DB.SqlServer.dll");
    
  3. 全名称

    Assembly assembly = Assembly.LoadFrom("DB.SqlServer.dll");
    
  4. 全名称

    Assembly assembly = Assembly.LoadFrom(@"D:\软谋教育\Ruanmou\Advanced13Encrypt\20191010Advanced13Course3Reflection\20191010Advanced13Course3Reflection\MyReflection\MyReflection\bin\Debug\DB.SqlServer.dll");
    

4.2 常规玩法

//1.动态加载dll
Assembly assembly = Assembly.Load("DB.SqlServer");// dll名称  默认到当前目录下查找

//获取所有类型
//assembly.GetTypes() 
//2.通过类型名称获取指定类型
Type type = assembly.GetType("DB.SqlServer.SqlServerHelper");

//3.创建对象
object oDbHelper = Activator.CreateInstance(type);
//dynamic dDbHelper = Activator.CreateInstance(type);
//dDbHelper.Query();

//4.类型转换
IDBHelper iDBHelper = oDbHelper as IDBHelper;

//5.调用方法
iDBHelper.Query();

第二步获取类型时,形式 = 命名空间名称 + 类名

第三步创建对象,其实和IDBHelper dBHelper = new SqlServerHelper(); 实例化一个对象等价,会执行到SqlServerHelper的构造函数里

经过第三步创建对象之后并不能直接调用Query方法,因为编译器就不认可(c# 是强类型语言);dynamic 是一个动态类型,可以避开编译器的检查,运行时检查,但存在安全问题 。

4.3 进阶玩法

封装 Factory + config 代码可以不用编译发布

简单工厂:

public class SimlpFactory
{ 
    private static string IDBHelperConfig = ConfigurationManager.AppSettings["IDBHelperConfig"]; 
    private static string DllName = IDBHelperConfig.Split(',')[1];
    private static string TypeName = IDBHelperConfig.Split(',')[0]; 
    public static IDBHelper CreateInstentce()
    {
        Assembly assembly = Assembly.Load(DllName);
        Type type = assembly.GetType(TypeName);
        object oDbHelper = Activator.CreateInstance(type);
        return oDbHelper as IDBHelper;
    }
}

config文件:

<appSettings>
    <add key="IDBHelperConfig" value="DB.Orcale.OrcaleHelper,DB.Orcale"/>
</appSettings>

调用:

IDBHelper iDBHelper = SimlpFactory.CreateInstentce();
iDBHelper.Query(); 

4.4 反射–选择不同的构造函数创建对象

//1.动态加载
Assembly assembly = Assembly.Load("DB.SqlServer");
//2.获取类型
Type type = assembly.GetType("DB.SqlServer.ReflectionTest");
//3.创建对象
object oDbHelper = Activator.CreateInstance(type);	//public ReflectionTest()
object oDbHelper1 = Activator.CreateInstance(type, new object[] { "杰克" });	// public ReflectionTest(string name)
object oDbHelper3 = Activator.CreateInstance(type, new object[] { 123 });	//public ReflectionTest(int id)

五、反射调用方法

5.1 反射–非类型转换调用方法

Assembly assembly = Assembly.Load("DB.SqlServer");
Type type = assembly.GetType("DB.SqlServer.ReflectionTest");

object oTest = Activator.CreateInstance(type);

无参数方法

MethodInfo show1 = type.GetMethod("show1");
show1.Invoke(oTest, null); //反射调用方法

有参数方法

MethodInfo show2 = type.GetMethod("show2");
show2.Invoke(oTest, new object[] { 123 }); //反射调用方法

重载方法,方法名称一样,参数不同

MethodInfo show1 = type.GetMethod("show1", new Type[] { typeof(string), typeof(int) });
show1.Invoke(oTest, new object[] { 123, "陈贺章" });

私有方法:

MethodInfo show4 = type.GetMethod("show4", BindingFlags.NonPublic | BindingFlags.Instance);
show4.Invoke(oTest, new object[] { "私有方法" });

静态方法: 对象的实例可以传入,也可以不传入。如果像常规使用是不需要实例化对象就可以直接调用方法(反射传null),但是反射却可以传入对象的实例

MethodInfo show5 = type.GetMethod("show5");
show5.Invoke(oTest, new object[] { "我是静态方法" });
show5.Invoke(null, new object[] { "我是静态方法" });

5.2 反射–调用泛型方法

六、反射黑科技

假设我们存在一个单例模式,代码如下:

/// <summary>
/// 单例模式:类,能保证在整个进程中只有一个实例
/// </summary>
public sealed class Singleton
{
    private static Singleton _Singleton = null;
    private Singleton()
    {
        Console.WriteLine("Singleton被构造");
    }

    static Singleton()
    {
        _Singleton = new Singleton();
    }

    public static Singleton GetInstance()
    {
        return _Singleton;
    }
}

常规用法:

Singleton singleton1 = Singleton.GetInstance();
Singleton singleton2 = Singleton.GetInstance();
Console.WriteLine(singleton1.Equals(singleton2)); //结果为:true

反射应用: 两次对象不一致,Activator.CreateInstance完全等价于new Singleton(),每次都调用了私有构造函数。反射破坏了单例,其实就是反射调用了私有构造函数

Assembly assembly = Assembly.Load("DB.SqlServer");
Type type = assembly.GetType("DB.SqlServer.Singleton");
object oSingleton1 = Activator.CreateInstance(type, true);   //第二个参数为 NoPublic--是否为公有的,完全等价于new Singleton();
object oSingleton2 = Activator.CreateInstance(type, true);
Console.WriteLine(oSingleton1.Equals(oSingleton2)); //结果为:false

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

跨文化合作:如何解决海外网红营销中的文化差异?

随着社交媒体的快速发展&#xff0c;海外网红营销已成为许多品牌和企业获取国际市场的有效方式。然而&#xff0c;由于不同国家和地区存在着独特的文化差异&#xff0c;如语言、价值观、习俗等&#xff0c;这也给品牌进行海外网红营销带来了一系列挑战。本文Nox聚星将和大家探讨…

WPF 自定义控件完成库容表盘显示效果

先看一下显示效果&#xff1a; 需要注意的地方有以下几点&#xff1a; 表盘的刻度分部&#xff0c;长刻度和短刻度显示。在数值80W时&#xff0c;需要更改刻度盘的颜色渐变。在数值80W时&#xff0c;更改库容总数背景的显示&#xff0c;也是颜色渐变。刻度盘控件属性定义&…

JVM中的堆和栈到底存储了什么

JVM数据区 先上一张Java虚拟机运行时数据区中堆、栈以及方法区存储数据的概要图&#xff0c;如下所示&#xff1a; 然后我们来具体解析一下堆和栈 堆 堆是存储时的单位&#xff0c;对于绝大多数应用来说&#xff0c;这块区域是 JVM 所管理的内存中最大的一块。线程共享&#…

JavaScript XHR、Fetch

1 前端数据请求方式 2 Http协议的解析 3 XHR的基本用法 4 XHR的进阶和封装 5 Fetch的使用详解 6 前端文件上传流程 早期的页面都是后端做好&#xff0c;浏览器直接拿到页面展示的&#xff0c;用到的是jsp、asp、php等等的语言。 这个叫做服务器端渲染SSR。 这里后端向前端…

[sqoop]导入数据

一、覆盖导入 例如维度表&#xff0c;每次导入的数据需要覆盖上次导入的数据。 hive-overwrite参数&#xff1a;实现覆盖导入 hive-import参数&#xff1a;表示向hive表导入 hive-table参数&#xff1a;指定目标hive库表 sqoop import \ --connect jdbc:mysql://hadoop1:3…

介绍性能压力测试的重要性

在当今数字化时代&#xff0c;软件和应用程序的性能对于用户体验和业务成功至关重要。为了确保系统在面临高负载和压力时能够正常运行&#xff0c;性能压力测试成为一项不可或缺的活动。本文将介绍性能压力测试的重要性。 性能压力测试是一种通过模拟实际场景中的负荷和用户访问…

前端两种实现轮播图方式

今天研究两种简单实现轮播图功能的方式。 目录 Layui实现轮播图 码云下载 提取静态文件 示例 注意 参数说明 改为轮播图 增加图片资源文件 轮播栏目修改 改为上下切换 切换事件 脚本中绑定改变事件 控制器查看 Swiper实现轮播图 下载swiper 下载到本地 加载sw…

EMC学习笔记(十七)PCB设计中的安规考虑

PCB设计中的安规考虑 1 概述2.安全标识2.1 对安全标示通用准则2.2 电击和能量的危险2.3 PCB上的熔断器2.4 可更换电池 3.爬电距离和电气间隙4.涂覆印制板4.1 PCB板的机械强度4.2 印制电路板的阻燃等级4.3 热循环试验与热老化试验4.4 抗电强度试验4.5 耐划痕试验 5.布线和供电 1…

网络安全(黑客)万字自学笔记

目录 特别声明&#xff1a; 一、前言 二、定义 三、分类 1.白帽黑客&#xff08;White Hat Hacker&#xff09; 2.黑帽黑客&#xff08;Black Hat Hacker&#xff09; 3.灰帽黑客&#xff08;Gray Hat Hacker&#xff09; 四、黑客文化 五、伦理问题 六、黑客的作用 …

shell脚本备份数据库

首先是在本地windows环境下尝试备份数据库 打开mysql的bin目录&#xff0c;然后在地址栏cmd&#xff0c;进入cmd界面&#xff0c;输入mysqldump命令&#xff0c;-u输入用户名&#xff0c;-p输入密码 还有数据库名称&#xff0c;以及后面要保存到的位置 mysqldump -uroot -p tes…

编写测试用例的方法,这个是真的很好用

大家测试过程中经常用的等价类划分、边界值分析、场景法等&#xff0c;并不能覆盖所有的需求&#xff0c;我们之前讲过很少用到的因果图法&#xff0c;下面就来讲另一种不经常用到但又非常重要的测试用例编写方法——测试大纲法。 测试大纲法适用于有多个窗口&#xff0c;每个…

Vue-Router相关理解4

两个新的生命周期钩子 activated和deactivated是路由组件所独有的两个钩子&#xff0c;用于捕获路由组件的激活状态具体使用 activated路由组件被激活时触发 deactivated路由组件失活时触发 src/pages/News.vue <template><ul><li :style"{opacity}&qu…

linux之Ubuntu系列(五)用户管理、查看用户信息 终端命令

创建用户 、删除用户、修改其他用户密码的终端命令都需要通过 sudo 执行 创建用户 设置密码 删除用户 sudo useradd -m -g 组名 新建用户名 添加新用户 -m&#xff1a;自动建立用户 家目录 -g&#xff1a;指定用户所在的组。否则会建立一个和用户同名的组 设置新增用户的密码&…

尝试-InsCode Stable Diffusion 美图活动一期

一、 Stable Diffusion 模型在线使用地址&#xff1a; https://inscode.csdn.net/inscode/Stable-Diffusion 二、模型相关版本和参数配置&#xff1a; 活动地址 三、图片生成提示词与反向提示词&#xff1a; 提示词&#xff1a;realistic portrait painting of a japanese…

vscode remote-ssh配置

使用vscode的插件remote-ssh进行linux的远程控制。 在vscode上安装完remote-ssh插件后&#xff0c;还需要安装openssh-client。 openssh-client安装 先win R打开cmd&#xff0c;输入ssh&#xff0c;查看是否已经安装了。 如果没有安装&#xff0c;用管理员权限打开powershe…

商城-学习整理-基础-环境搭建(二)

目录 一、环境搭建1、安装linux虚拟机1&#xff09;下载&安装 VirtualBox https://www.virtualbox.org/&#xff0c;要开启 CPU 虚拟化2&#xff09;虚拟机的网络设置3&#xff09;虚拟机允许使用账号密码登录4&#xff09;VirtualBox冲突5&#xff09;修改 linux 的 yum 源…

DirectX12(D3D12)基础教程(二十二) ——HDR IBL 等距柱面环境光源加载和解算及 GS 一次性渲染到 CubeMap

前序文章目录 DirectX12&#xff08;D3D12&#xff09;基础教程&#xff08;一&#xff09;——基础教程 DirectX12&#xff08;D3D12&#xff09;基础教程&#xff08;二&#xff09;——理解根签名、初识显存管理和加载纹理、理解资源屏障 DirectX12&#xff08;D3D12&…

传统软件测试过程中的测试分工

最近看了点敏捷测试的东西&#xff0c;看得比较模糊。一方面是因为没有见真实的环境与流程&#xff0c;也许它跟本就没有固定的模式与流程&#xff0c;它就像告诉人们要“勇敢”“努力”。有的人在勇敢的面对生活&#xff0c;有些人在勇敢的挑战自我&#xff0c;有些人在勇敢的…

Java打怪升级路线的相关知识

第一关:JavaSE阶段 1、计算机基础 2、java入门学习 3、java基础语法 4、流程控制和方法 5、数组 6、面向对象编程 7、异常 8、常用类 9、集合框架 10、IO 11、多线程 12、GUI编程 13、网络编程 14、注解与反射 15、JUC编程 16、JVM探究 17、23种设计模式 18、数据结构与算法 1…

修复git diff正文中文乱码

Linux git diff正文中文乱码 在命令行下输入以下命令&#xff1a; $ git config --global core.quotepath false # 显示 status 编码 $ git config --global gui.encoding utf-8 # 图形界面编码 $ git config --global i18n.commit.encoding utf-8 # …