C#-反射

一、概念

  1. 反射(Reflection)在C#中是一种非常重要的特性,它为开发者提供了在运行时获取和操作关于类型、成员、属性、方法等的详细信息的能力。通过反射,开发者可以在程序运行期间动态地创建对象、调用方法、设置属性值以及进行其他多种操作,而不需要事先在代码中硬编码这些操作。
  2. C#中的反射API主要集中在System.Reflection命名空间下,该命名空间包含了多个用于反射的类和接口。

1.1常用的反射类型:

  1. Assembly(程序集):Assembly类用于表示一个程序集,可以加载和卸载程序集,以及获取程序集中的类型、资源等信息。
  2. Type(类型):Type类表示程序集中的任意类型,可以是类、接口、枚举、结构等。通过Type类可以获取类型的成员信息,如方法和属性等。
  3. MethodInfo(方法信息):MethodInfo类提供了关于特定方法的信息,包括方法的名称、参数、返回类型等,还可以调用该方法。
  4. PropertyInfo(属性信息):PropertyInfo类用于表示属性,可以获取或设置属性的值。
  5. FieldInfo(字段信息):FieldInfo类用于表示字段,可以获取或设置字段的值。

1.2常用场景

  1. 动态加载程序集:在运行时根据需要加载.dll或.exe文件,并访问其中的类型和成员。
  2. 晚期绑定和早期绑定:晚期绑定是指在程序运行时才确定要调用的方法或访问的属性,而早期绑定是在编译时就确定。反射可以实现晚期绑定。
  3. 对象序列化和反序列化:在序列化和反序列化过程中,反射可以动态地读取和写入对象的各个属性。
  4. 实现插件系统:通过反射可以加载和调用插件中的功能,而无需预先知道插件的具体实现细节。
  5. 单元测试和ORM(对象关系映射)技术:在单元测试中,反射可用于动态调用待测试的方法;在ORM技术中,反射则可用于动态访问和映射对象的属性至数据库表的列。

1.3程序集概念

  1. 程序集(Assembly)是.NET框架下代码编译的一个逻辑单元,用于组合相关的代码和类型,最终生成PE文件
  2. 程序集是.NET应用程序的基本组成单元,它以可执行文件(.exe)动态链接库文件(.dll)的形式存在。一个程序集可以包含一个或多个模块,每个模块又可以包括多个类型(如类、接口、枚举等)。程序集不仅包含编译后的IL(中间语言)代码,还包含描述程序集自身的元数据、资源文件等信息。
  3. 在软件开发过程中,一个项目通常对应一个程序集。例如,在Visual Studio中,一个解决方案可以包含多个项目,每个项目编译后都会生成一个程序集(一般是一个.dll文件)。这些程序集可以被其他项目引用,从而实现功能的复用和模块化开发。

二、Type类

2.1、概念

Type类是C#反射中的一个核心类,用于表示所有.NET类型的公共基类。通过Type类,可以访问类型的元数据,获取类型的成员信息,并进行实例化等操作。以下将详细探讨Type类的各个方面:
定义:Type类位于System.Reflection命名空间下,它提供了很多方法来获取类型信息、创建实例、执行方法等。

2.2、功能

通过Type类,可以检查类型是否为泛型类型、接口、委托等。同时,还可以获取类型的属性、方法、事件等成员信息。

2.3、使用

  1. 获取Type实例
    静态方法:可以使用Type的静态方法如GetType、GetTypes等来获取类型信息。例如,typeof(int)返回表示int类型的Type对象。
    动态实例:通过对象实例调用GetType()方法获取其类型。例如,一个字符串实例myString.GetType()将返回String类型的Type对象。
  2. 获取类型信息
    属性和方法:使用Type对象的GetProperties和GetMethods方法可以分别获取类型的所有属性和方法。这些返回的PropertyInfo和MethodInfo对象提供了更详细的成员信息。
    基类和接口:可以通过Type对象的BaseType属性获取其基类的类型,IsClass属性判断是否为类,Implements接口方法检查实现的接口。
  3. 创建类型实例
    使用Activator类:Activator类提供了基于Type创建实例的方法,如CreateInstance。这允许在运行时动态创建对象。
    使用ConstructorInfo:通过Type对象的GetConstructors方法获取构造函数,然后使用ConstructorInfo对象的Invoke方法创建实例。
  4. 调用类型方法
    MethodInfo对象:通过Type对象的GetMethods获取方法列表,然后用MethodInfo对象的Invoke方法动态调用这些方法。
    参数处理:MethodInfo的Invoke方法需要参数数组。如果方法需要的参数类型与传递的参数类型不匹配,需要进行适当的类型转换。
  5. 类型的兼容性和转换
    类型兼容检查:IsAssignableFrom方法可以用来检查一个类型是否可以从另一个类型派生。例如,typeof(object).IsAssignableFrom(typeof(string))将返回true。
    类型转换:Type类提供了几个方法来检查和执行类型转换,如IsInstanceOfType方法和ChangeType方法。

2.4、代码实例

// See https://aka.ms/new-console-template for more information
//Console.WriteLine("Hello, World!");
//反射
//Type类实例化using System.Reflection;Type type = typeof(Person);
Console.WriteLine("--------------------获取属性信息------------------------------");
Console.WriteLine(type.ToString());
//01、获取类中属性信息GetProperties(),返回的是PropertyInfo[]类型数组
PropertyInfo[] propertyInfos=type.GetProperties();
foreach (var item in propertyInfos)
{Console.WriteLine(item.Name);Console.WriteLine(item.Attributes);//还有其他特性可以获取
}
Console.WriteLine("-----------------------获取字段信息---------------------------");//02、获取所有字段,只能获取共有字段,属性的第二种定义方式也属于私有字段
FieldInfo[] fieldInfos=type.GetFields();
foreach (var field in fieldInfos) { Console.WriteLine(field.Name);Console.WriteLine(field.FieldType);
}/*结果:
iD
System.Int32
name
System.String*/Console.WriteLine("-----------------------获取类的信息---------------------------");
//直接使用type调用相关方法
Console.WriteLine(type.Name);
Console.WriteLine(type.Attributes);Console.WriteLine("-----------------------获取构造方法的信息---------------------------");
ConstructorInfo[] methodInfo =type.GetConstructors();
foreach (var item in methodInfo)
{Console.WriteLine(item);//返回的是构造方法的签名,返回值与参数
}
/* 
Void.ctor()
Void.ctor(Int32, System.String)*/
Console.WriteLine("-----------------------获取方法的信息---------------------------");
MethodInfo[] info = type.GetMethods();foreach (var item in info)
{Console.WriteLine(item.Name);
}/// <summary>
/// 定义反射测试类
/// </summary>
class  Person{//01、定义公有字段public int iD;public string name;//02、定义私有字段private string Sex;//03、定义属性,属性有两种定义方式//方式1public int Age { get; set; }//方式2private string address;//04无参构造public Person(){Console.WriteLine("无参构造");}//05有参构造public Person(int iD, string name){this.iD = iD;this.name = name;}public string Address { get { return this.address; }   set { this.address = value; }}
}

三、Assembly类

3.1概念及使用

Assembly类在C#反射中是用于操作程序集的一个重要工具,它能够加载、枚举、和操纵程序集。以下是对Assembly类的详细解析:

  1. 加载程序集
    • 静态加载方法:Assembly.Load方法可以加载一个程序集,通常用于加载同一目录下或全局程序集中的其它程序集[1]。例如,Assembly assembly = Assembly.Load("AssemblyName");
    • 动态加载方法:Assembly.LoadFrom和Assembly.LoadFile方法可以指定文件路径来加载程序集。例如,Assembly assembly = Assembly.LoadFrom("包含程序集清单的文件的名称或路径");[2]。
  2. 枚举程序集中的类型
    • 获取类型信息:通过Assembly实例的GetTypes方法可以得到程序集中定义的所有类型的Type对象数组[3]。例如,Type[] types = assembly.GetTypes();
    • 创建类型实例:结合Activator.CreateInstance方法,可以用枚举到的Type动态创建类型的实例。例如,object obj = Activator.CreateInstance(type);[4]。
  3. 调用方法与访问属性
    • 调用方法:MethodInfo类可以用于获取方法和动态调用方法。例如,使用MethodInfo method = type.GetMethod("MethodName");来获取方法信息,然后使用method.Invoke(obj, parameters);来调用它[4]。
    • 访问属性:PropertyInfo类可以用来获取属性信息和动态设置或获取属性值。例如,PropertyInfo property = type.GetProperty("PropertyName");用来获取属性信息,然后property.SetValue(obj, value, index);用于设置属性值[4]。
  4. 获取程序集信息
    • 获取程序集名称和版本:Assembly实例的GetName方法可以获取程序集的完整名称、版本等信息。例如,AssemblyName name = assembly.GetName();[4]。
    • 获取程序集元数据:可以使用Assembly实例的其他方法如GetCustomAttributes来检查程序集的特性和元数据[4]。
  5. 操纵程序集
    • 创建新实例:除了加载已存在的程序集外,Assembly类还提供了CreateInstance方法用于创建程序集中特定类型的新实例[3]。例如,object instance = assembly.CreateInstance("TypeName");
    • 获取当前程序集:Assembly.GetExecutingAssembly方法可以获取当前正在执行的程序集,这在需要访问当前程序集中的资源或类型时非常有用[2]。例如,Assembly currentAssembly = Assembly.GetExecutingAssembly();

3.2应用-Assembly拿到成员-Type操作成员

3.2.1、Assembly获取程序集中的类/接口

  1. VS中定义类库文件:demo49反射_数据,作为程序集文件
  2. 定义控制台应用程序:demo49反射_Assembly使用,通过反射 去获取指定程序集里面的程序 操作成员(注意:此文将并没有对程序集文件添加依赖,不可直接操作程序集文件中内容,需要使用反射相关技术获取程序集文件在这里插入图片描述
  3. 通过LoadFile获取程序集文件:通过loadFile+程序集的绝对路径的方式 获取到了要反射的程序集
    在这里插入图片描述
Assembly ass= Assembly.LoadFile(@"E:\c#体系课\04c#高级\代码\demo49反射_数据\bin\Debug\net7.0\demo49反射_数据.dll");

在这里插入图片描述
4. 通过Assembly对象 自己程序的GetType获取程序集中指定的类型 成员 GetType()+类型全名称 [命名空间.类型名字]

//通过GetTypes() 获取程序集中 所以的成员   获取到的是  pubic + internal
Type type = ass.GetType("demo49反射_数据.Person");//获取单个
Type[] types = ass.GetTypes();//获取所有

但是有时候我们并不希望获取内部internal成员

//只获取程序集中被public修饰的成员,不要internal成员  Exported:出口
Type[] types = ass.GetExportedTypes();foreach (var item in types)
{Console.WriteLine(item.Name);
}

在这里插入图片描述
在这里插入图片描述

3.2.2、Type获取类中方法

  1. 添加数据,对Person类中添加需要测试的属性,方法等数据
public class  Person
{//01、定义属性public string Name { get; set; }public int Age { get; set; }public char Gender { get; set; }//02、定义方法public static void SayHello(){Console.WriteLine("我是静态方法");}public void Sayhi(){Console.WriteLine("我是实例方法");}public int Add(int n1, int n2) {return n1 + n2;}public Person(){}//03、有参构造public Person(string name,int age,char gender){this.Name = name;this.Age = age;this.Gender = gender;}public void Test() {Console.WriteLine("我是无参方法");}public void Test(string name) {Console.WriteLine(name);}public void Test(int n1,int n2){Console.WriteLine(n1+n2);}}
  1. 获取类中方法
//01、Assembly操作
Assembly ass= Assembly.LoadFile(@"D:\C#_Project\05C#高级代码\demo49反射_数据\bin\Debug\net7.0\demo49反射_数据.dll");//02、Type操作
Type type = ass.GetType("demo49反射_数据.Person");
//获取所有方法
MethodInfo[] methodInfos = type.GetMethods();
foreach (var item in methodInfos)
{Console.WriteLine(item.Name);
}

在这里插入图片描述

添加限定条件,只要静态方法

//如果我们要用BindingFlags.Static这个枚举 需要再加上一个public 枚举进行配合
MethodInfo[] methodInfos = type.GetMethods(BindingFlags.Static | BindingFlags.Public);
foreach (var item in methodInfos)
{Console.WriteLine(item.Name);
}//如果只要其中一个方法就可以用下面这种方式
MethodInfo method = type.GetMethod("SayHello");

在这里插入图片描述

3.2.3、执行获取的方法

如果执行静态方法 注意:不可以像调用委托或者事件一样通过加括号直接调用
间接调用 通过 invoke调用

//第一个参数 在调用成员的是很好 是否需要传入对象
//1.静态方法:类.
//2.实例方法:对象.
//第二个参数 调用方法的时候是否需要传入参数 如果需要就传入参数 不需要传入null就可以了

 method.Invoke(null,null);

在这里插入图片描述

3.2.4、Activator创建对象(方法一)

对象创建唯一途径就是构造方法被调用

//01、获取程序集
Assembly ass= Assembly.LoadFile(@"D:\C#_Project\05C#高级代码\demo49反射_数据\bin\Debug\net7.0\demo49反射_数据.dll");//02、获取程序集中需要的类
Type type = ass.GetType("demo49反射_数据.Person");//03、创建person对象实例  在反射中 一般有2中方式创建对象 
//1.调用构造函数
//2.通过Activator 动态创建实例对象
//Activator的底层 就是调用无参构造函数  
object person = Activator.CreateInstance(type);
Console.WriteLine(person.ToString());//调用实例方法,参数1:实例对象
method.Invoke(person,null);

注意:SayHello是静态方法直接通过类就可获取
在这里插入图片描述

3.2.5、构造方法创建对象(方法二)

//LoadFile传入的路径是绝对路径    不是相对路径
//1.通过loadFile+程序集的绝对路径的方式 获取到了要反射的程序集,从文件中找,不要直接属性复制
Assembly ass= Assembly.LoadFile(@"D:\C#_Project\05C#高级代码\demo49反射_数据\bin\Debug\net7.0\demo49反射_数据.dll");
Type type = ass.GetType("demo49反射_数据.Person");
//获取单个构造函数
//参数:string name,int age,char gender
ConstructorInfo constructor= type.GetConstructor(new Type[] { typeof(string),typeof(int),typeof(char)});
object person= constructor.Invoke(new object[] {"张三",10,'男'});
//间接调用  person返回的就是你创建的对象PropertyInfo property= type.GetProperty("Name");//通过GetValue()调用这个方法可以获取属性的值
object propertys= property.GetValue(person);
Console.WriteLine(propertys);//输出:张三

3.2.6、获取有参有返回值方法

//01、获取程序集
Assembly ass= Assembly.LoadFile(@"D:\C#_Project\05C#高级代码\demo49反射_数据\bin\Debug\net7.0\demo49反射_数据.dll");
//02、通过Assembly对象 自己程序的GetType获取程序集中指定的类型 成员  GetType()+类型全名称 [命名空间.类型名字]
Type type = ass.GetType("demo49反射_数据.Person");
//3.1、创建类对象
object person = Activator.CreateInstance(type);
//3.2、通过Type数组 判断调用方法的参数个数和参数类型  依次告诉我们编译器我们想要哪个方法
MethodInfo method = type.GetMethod("Test", new Type[] { });MethodInfo method1 = type.GetMethod("Test", new Type[] { typeof(string) });MethodInfo method2= type.GetMethod("Test", new Type[] { typeof(int), typeof(int) });//04、调用重载方法 编译器无法直接从是否传递的参数来潘顿我们调用的是哪个方法
Console.WriteLine(method.Name);
method.Invoke(person,new object[] { });Console.WriteLine(method1.Name);
method1.Invoke(person,new object[] {10 });Console.WriteLine(method2.Name);
method2.Invoke(person,new object[] { 10,20 });

四、反射常用API

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

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

相关文章

【Java开发实训】day01

目录 1.Java开发步骤 2.目录的三个表达方法 3.Java的三种注释方法 4.文档注释的作用 &#x1f308;嗨&#xff01;我是Filotimo__&#x1f308;。很高兴与大家相识&#xff0c;希望我的博客能对你有所帮助。 &#x1f4a1;本文由Filotimo__✍️原创&#xff0c;首发于CSDN&…

运维锅总详解数据一致性

本文首先对数据一致性进行简要说明&#xff0c;然后画图分析展示9种数据一致性协议的工作流程&#xff0c;最后给出实现这9种协议的例子。希望对您理解数据一致性有所帮助&#xff01; 一、数据一致性简介 数据一致性是数据库和分布式系统中的一个关键概念&#xff0c;它确保…

【Mac】Folder Icons for mac(文件夹个性化图标修改软件)软件介绍

软件介绍 Folder Icons for Mac 是一款专为 macOS 设计的应用程序&#xff0c;主要用于个性化和定制你的文件夹图标。以下是它的主要特点和使用方法&#xff1a; 主要特点&#xff1a; 个性化文件夹图标 Folder Icons for Mac 允许用户为 macOS 上的任何文件夹定制图标。你…

怎样优化 PostgreSQL 中对布尔类型数据的查询?

文章目录 一、索引的合理使用1. 常规 B-tree 索引2. 部分索引 二、查询编写技巧1. 避免不必要的类型转换2. 逻辑表达式的优化 三、表结构设计1. 避免过度细分的布尔列2. 规范化与反规范化 四、数据分布与分区1. 数据分布的考虑2. 表分区 五、数据库参数调整1. 相关配置参数2. 定…

融云入驻首个数字生态出海基地,加速构建数字经济出海创新生态

7 月 3 日&#xff0c;“2024 全球数字经济大会”重要专题论坛“2024 数字生态出海发展论坛”在北京国家会议中心举行。 论坛由全球数字经济大会组委会主办&#xff0c;北京市经济和信息化局、北京市政务服务和数据管理局、大兴区人民政府共同承办。来自阿联酋、日本、古巴、…

Chain-of-Verification Reduces Hallucination in Lagrge Language Models阅读笔记

来来来&#xff0c;继续读文章了&#xff0c;今天这个是meta的研究员们做的一个关于如何减少LLM得出幻觉信息的工作&#xff0c;23年底发表。文章链接&#xff1a;https://arxiv.org/abs/2309.11495 首先&#xff0c;这个工作所面向的LLM的问答任务&#xff0c;是list-based q…

动态粒子发射特效404网站HTML源码

源码介绍 动态粒子发射404网站HTML源码&#xff0c;粒子内容可以进行修改&#xff0c;默认是4&#xff0c;0数字还有一个页面不存在英文&#xff0c;可以自行修改&#xff0c;喜欢的朋友可以拿去使用&#xff0c;源码是html&#xff0c;记事本打开修改即可&#xff0c;鼠标双击…

线程池的合理使用

线程池的合理使用 一、简介二、为什么要使用线程池三、核心参数四、如何合理配置线程参数1.1 corePoolSize && maximumPoolSize1.2 Handler 拒绝策略1.2.1AbortPolicy&#xff1a;优势&#xff1a;劣势&#xff1a; 1.2.2 DiscardPolicy&#xff1a;优势&#xff1a;劣…

海外媒体发稿-全媒体百科

全球知名媒体机构 在全球范围内&#xff0c;有许多知名的新闻机构负责报道世界各地的新闻事件。以下是一些国外常见的媒体机构&#xff1a; AP&#xff08;美联社&#xff09;合众国际社&#xff08;UPI&#xff09;AFP(法新社)EFE&#xff08;埃菲通讯社&#xff09;Europa …

Nginx理论篇与相关网络协议

Nginx是什么&#xff1f; Nginx是一款由C语言编写的高性能、轻量级的web服务器&#xff0c;一个线程能处理多个请求&#xff0c;支持万级并发。 优势&#xff1a;I/O多路复用。 I/O是什么&#xff1f; I指的是输入&#xff08;Input&#xff09;,O是指输出&#xff08;Outp…

【安全设备】日志审计

一、什么是日志审计 日志审计是一站式的日志数据管理平台&#xff0c;主要致力于提供事前预警、事后审计的安全能力&#xff0c; 通过对日志数据的全面采集、解析和深度的关联分析&#xff0c;及时发现各种安全威胁和异常行为事件。日志审计是指通过集中采集信息系统中的各类信…

解决:Android Studio 突然打不开!提示Failed to create JVM:error code -1

Android studio1.5 一直用得好好的&#xff0c;突然有一天打不开&#xff0c;并提示&#xff1a; 可是系统配置中&#xff0c;java的配置也是正常的。 解决方法&#xff1a; 修改安装目录下的studio64.exe.vmoptions 文件 直接将文件内容改成&#xff1a; -Xms128m -Xmx512m…

谷歌+火狐浏览器——实现生成二维码并实现拖动——js技能提升

最新遇到的问题&#xff1a;前两个二维码拖动不了&#xff0c;只有第三个一维码生成后&#xff0c;才可以拖拽 【问题】&#xff1a;出现在都是绝对定位&#xff0c;但是没有指定z-index导致的。 解决办法&#xff1a;在方法中添加一个变量 renderDrag(id) {var isDragging f…

Python CuPy库:GPU加速的科学计算

更多Python学习内容&#xff1a;ipengtao.com 在数据科学和机器学习领域&#xff0c;处理大规模数据集常常需要巨大的计算资源。Python的CuPy库通过提供一个类似NumPy但运行在NVIDIA GPU上的接口&#xff0c;大幅提升了数组操作的速度&#xff0c;使得复杂的数值计算变得更加高…

基于FPGA的图像边缘检测(OV5640)

一、简介 1.应用范围 边缘主要存在于图像中目标与目标之间&#xff0c;目标与背景之间&#xff0c;区域与区域之间。 边缘检测的目的就是找到图像中亮度变化剧烈的像素点构成的集合&#xff0c;表现出来往往是轮廓。如果图像中边缘能够精确的测量和定位&#xff0c;那么&…

GaussDB关键技术原理:高性能(四)

GaussDB关键技术原理&#xff1a;高性能&#xff08;三&#xff09;从查询重写RBO、物理优化CBO、分布式优化器、布式执行框架、轻量全局事务管理GTM-lite等五方面对高性能关键技术进行了解读&#xff0c;本篇将从USTORE存储引擎、计划缓存计划技术、数据分区与分区剪枝、列式存…

Redis 7.x 系列【19】管道

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 往返时间2. 管道技术3. 代码演示4. 其他批处理4.1 原生批处理命令4.2 事务4.3 脚本…

240708_昇思学习打卡-Day20-MindNLP ChatGLM-6B StreamChat

240708_昇思学习打卡-Day20-MindNLP ChatGLM-6B StreamChat 基于MindNLP和ChatGLM-6B实现一个聊天应用&#xff0c;本文进行简单记录。 环境配置 %%capture captured_output # 实验环境已经预装了mindspore2.2.14&#xff0c;如需更换mindspore版本&#xff0c;可更改下面mi…

Java | Leetcode Java题解之第224题基本计算器

题目&#xff1a; 题解&#xff1a; class Solution {public int calculate(String s) {Deque<Integer> ops new LinkedList<Integer>();ops.push(1);int sign 1;int ret 0;int n s.length();int i 0;while (i < n) {if (s.charAt(i) ) {i;} else if (s…

实施OPC UA网关以加速设备与MES系统之间的连接

在现代工业自动化中&#xff0c;信息化和智能化已成为企业提升竞争力的关键因素&#xff0c;为了实现生产过程的自动化和管理的高效化&#xff0c;工业自动化系统&#xff08;如OPC UA&#xff09;与制造执行系统&#xff08;MES&#xff09;的集成变得尤为重要。OPC UA&#x…