C#反射机制介绍

文章目录

  • 简介
  • 一、什么是反射
  • 二、反射的用途
  • 三、反射用到的命名空间及主要类
  • 四、Type类
  • 五、Assembly类
  • 六、使用反射实现上面的程序
  • 七、反射的优缺点


简介

这篇文章介绍了C#的反射机制,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
先看下面一个动物点名系统的简单例子:

有一个Animal的抽象动物父类,里面定义了Name、Age两个属性和一个Shout()方法,Animal类定义如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace Animal
{/// <summary>/// 抽象父类/// </summary>public abstract class Animal{/// <summary>/// Name属性/// </summary>public string Name { get; set; }/// <summary>/// Age属性/// </summary>public int Age { get; set; }/// <summary>/// Shout抽象方法/// </summary>public abstract void Shout();}
}

分别定义Cat、Dog类继承自Animal类,Cat类定义如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace Animal
{public class Cat :Animal{/// <summary>/// 构造函数初始化/// </summary>public Cat(){base.Name = "汤姆";base.Age = 2;}public override void Shout(){Console.WriteLine("喵喵喵,我是{0},今年{1}岁",base.Name,base.Age);}}
}

Dog类定义如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace Animal
{public class Dog : Animal{/// <summary>/// 构造函数初始化/// </summary>public Dog(){base.Name = "布鲁斯";base.Age = 3;}public override void Shout(){Console.WriteLine("汪汪汪,我是{0},今年{1}岁",base.Name, base.Age);}}
}

应用场景:在一个控制台程序中,输入具体的动物的类型,根据输入的动物类型,输出Name、Age和Shout()方法,使用传统方式实现的代码如下:

using Animal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;namespace ReflectionCon
{class Program{static void Main(string[] args){Console.WriteLine("请录入动物类型:");string type = Console.ReadLine().Trim();Animal.Animal a = null;switch (type){case "cat":a = new Cat();a.Shout();break;case "dog":a = new Cat();a.Shout();break;}Console.ReadKey();}}
}

程序运行结果如下:
在这里插入图片描述
那么问题来了:如果我们想要增加一个动物类型,那么就需要修改现有的代码,在switch里面增加判断。但是这种方式很不利于以后的维护,违反了开闭原则,每次增加一个动物类型的时候,都需要修改代码。那么有没有其他方式可以做到不用修改代码就可以实现呢?答案是肯定的,那就是使用我们接下来要讲的反射,先来了解一下什么是反射。

一、什么是反射

在讲解什么是反射之前,先来了解应用程序的结构。

程序代码在编译后生成可执行的应用,我们首先要了解这种可执行应用程序的结构。

应用程序结果分为应用程序域-程序集-模块-类型-成员几个层次,公共语言运行时(CLR)加载器管理应用程序域,这种管理包括将每个程序集加载到相应的应用程序域以及控制每个程序集中类型层次结构的内存布局。

程序集包含模块,而模块包含类型,类型又包含成员,反射则提供了封装程序集、模块和类型的对象。我们可以使用反射动态地创建类型的实例,将类型绑定到现有对象或从现有对象中获取类型,然后调用类型的方法或访问其字段和属性。

那么究竟什么是反射呢?

反射(Reflection)是.NET中的重要机制,可以在运行时获得.NET中每一个类型(包括类、结构、委托、接口和枚举等)的成员,包括方法、属性、事件、以及构造函数等。还可以获得每个成员的名称、限定符和参数等。有了反射,即可对每一个类型了如指掌。如果获得了构造函数的信息,即可直接创建对象,即使这个对象的类型在编译时还不知道。

二、反射的用途

1、使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。

2、使用Module了解包含模块的程序集以及模块中的类等,还可以获取在模块上定义的所有全局方法或其他特定的非全局方法。

3、使用ConstructorInfo了解构造函数的名称、参数、访问修饰符(如public或private)和实现详细信息(如abstract或virtual)等。使用Type的GetConstructors()或GetConstructor()方法来调用特定的构造函数。

4、使用MethodInfo了解方法的名称,返回类型、参数、访问修饰符(如public或private)和实现详细信息(如abstract或virtual)等。使用Type的GetMethods()或GetMethod()方法来调用特定的方法。

5、使用FiledInfo了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。

6、使用EventInfo了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。

7、使用PropertyInfo了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。

8、使用ParameterInfo了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。

三、反射用到的命名空间及主要类

1、命名空间

System.Reflection
System.Type
System.Reflection.Assembly

2、反射用到的主要类

Type类:该类位于System.Type命名空间下面,通过这个类可以访问任何给定数据类型的信息。

Assembly类:该类位于System.Reflection.Assembly命名空间下面,通过这个类可以访问给定程序集的信息,或者把这个程序集加载到程序中。

四、Type类

Type类位于System.Type命名空间下面,通过这个类可以访问关于任何数据类型的信息。

我们以前把Type看作一个类,但它实际上是一个抽象的基类。只要实例化了一个Type对象,实际上就实例化了Type的一个派生类。尽管一般情况下派生类只提供各种Type方法和属性的不同重载,但是这些方法和属性返回对应数据类型的正确数据,Type有与每种数据类型对应的派生类。

它们一般不添加新的方法或属性。通常,获取指向任何给定类型的Type引用有3种常用方式:

1、使用GetType()方法,所有的类都会从System.Object继承这个方法
string v = “abc”;
Type type = v.GetType();
2、使用Type类的静态方法GetType()
Type type2 = Type.GetType(“System.string”, false, true);
3、使用C#的typeof运算符,这个运算符的参数是类型的名称(但不放在引号中)
var t = typeof(string);
运行结果:
在这里插入图片描述
注意:在一个变量上调用GetType()方法,不是把类型的名称作为其参数。但要注意,返回的Type对象仍只与该数据类型相关。如果引用了一个对象,但不能确保该对象实际上是哪个类型的实例,这个方法就很有用。

4、Type类的属性

由Type实现的属性可以分为下述三类:

1)许多属性都可以获取包含与类相关的各种名称的字符串
在这里插入图片描述
2)属性还可以进一步获取Type对象的引用,这些引用表示相关的类
在这里插入图片描述
3)许多Boolean 属性表示这个类型是一个类、还是一个枚举等。这些属性包括IsAbstract、IsArray、IsClass、IsEnum、IsInterface、IsPointer、IsPrimitive(一种预定义的基本数据类型)、 IsPublic、IsSealed和IsValueType
在这里插入图片描述

5、Type类的方法

System.Type类的大多数方法都用于获取对应数据类型的成员信息:构造函数、属性、方法和事件等。它有许多方法,但它们都有相同的模式。

例如,有两个方法可以获取数据类型的方法信息:GetMethod() 和 GetMethods()。GetMethod()方法返回System.Reflection.MethodInfo对象的一个引用,其中包含一个方法的信息。GetMethods()返回这种引用的一个数组。其区别是GetMethods()返回所有方法的信息,而GetMethod()返回一个方法的信息,其中该方法包含特定的参数列表。这两个方法都有重载方法,该重载方法有一个附加的参数,BindingFlags枚举值,表示应返回哪些成员,例如,返回公有成员、实例成员和静态成员等。
在这里插入图片描述

Type的成员方法:
在这里插入图片描述
注意:GetMember() 和 GetMembers()方法返回数据类型的一个或所有成员的信息,这些成员可以是构造函数、属性和方法等。最后要注意,可以调用这些成员,其方式是调用Type的InvokeMember()方法,或者调用MethodInfo, PropertyInfo和其他类的Invoke()方法。

五、Assembly类

Assembly类在System.Reflection名称空间中定义,它允许访问给定程序集的元数据,它也包含可以加载和执行程序集(假定该程序集是可执行的)的方法。与Type类一样,Assembly类包含非常多的方法和属性。

在使用Assembly实例做一些工作前,需要把相应的程序集加载到正在运行的进程中。为此,可以使用静态成员Assembly.Load()或Assembly.LoadFrom()这两个方法的区别是:

Load()方法的参数是程序集的名称,运行库会在各个位置上搜索该程序集,试图找到该程序集,这些位置包括本地目录和全局程序集缓存。使用Load()方法前要添加程序集的引用。

LoadFrom()方法的参数是程序集的完整路径名,它不会在其他位置搜索该程程序集。

例如:
Assembly assembly1 = Assembly.Load(“Animal”);
Assembly assembly1 = Assembly.LoadFrom(@“D:\Study\Practice\Animal.dll”);
这两个方法都有许多其他重载版本,它们提供了其他安全信息。加载了一个程序集后,就可以使用它的各种属性进行查询,例如,查找它的全名:
string name = assembly1.FullName;
Assembly类的一个功能是它可以获得在相应程序集中定义的所有类型的详细信息,只要调用Assembly以GetTypes()方法,它就可以返回一个包含所有类型的详细信息的Type类型的引用数组:
Type[] types = assembly.GetTypes();
foreach (Type definedType in types)
(
//处理代码
)

六、使用反射实现上面的程序

经过上面的讲解,相信大家对反射有一定的了解了,下面将会使用反射实现开篇提到的应用场景,代码如下:

using Animal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;namespace ReflectionCon
{class Program{static void Main(string[] args){Console.WriteLine("请录入动物类型:");string type = Console.ReadLine().Trim();// 创建程序集对象,静态加载Animal程序集  前提:需要先添加对Animal程序集的引用Assembly assembly = Assembly.Load("Animal");// 获取程序集中的类型(在这里指的就是Animal里面的类:即Cat、Dog、Pig、Bird类)Type[] types = assembly.GetTypes();foreach (Type t in types){// t.Name表示类名(即Cat、Dog、Pig、Bird)if (type == t.Name.ToLower()){// 找到Shout方法MethodInfo m = t.GetMethod("Shout");// 创建对象object o = Activator.CreateInstance(t);// 找属性PropertyInfo[] para = t.GetProperties();// 遍历属性foreach (PropertyInfo p in para){// 输出属性的名字 即:Name和Age//Console.WriteLine(p.Name);if (p.Name == "Name"){// 给属性赋值p.SetValue(o, "张三", null);}if (p.Name == "Age"){// 获取o对象的属性为p的属性值并加10int age = Convert.ToInt32(p.GetValue(o)) + 10;// 给属性赋值p.SetValue(o, age, null);}}// 调用方法m.Invoke(o, null);}}Console.ReadKey();}}
}

运行程序:
在这里插入图片描述

如果新增加一个动物类,只需要实现Animal抽象父类即可,而主程序不需要修改。

七、反射的优缺点

1、反射的优点
1)、反射提高了程序的灵活性和扩展性。
2)、降低耦合性,提高自适应能力。
3)、它允许程序动态创建和控制任何类的对象,无需提前硬编码目标类。适用在程序集不固定的地方,通常和配置文件一起使用。

2、反射的缺点
1)、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
2)、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。

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

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

相关文章

2024北京智源大会

北京智源大会是年度国际性人工智能高端学术交流的盛会&#xff0c;定位于内行的AI盛会。智源大会紧密围绕当前人工智能学术领域迫切需要解决的问题&#xff0c;以及产业落地过程中存在的诸多挑战&#xff0c;开展深入探讨。智源研究院是2018年11月份成立的一家人工智能领域的新…

Nginx 重启失败nginx: [alert] kill(3259, 1) failed (3: No such process)

Nginx 重启失败 问题描述 // 在nginx的sbin 目录下重启nginx 报以下异常 [root192 sbin]# ./nginx -s reload nginx: [alert] kill(3259, 1) failed (3: No such process)问题解决 1&#xff0c;说是找不到nginx的配置文件 2&#xff0c;需要重新加载下nginx的配置文件即可命…

JAVA 根据提供的本地class路径转换为class对象

JAVA 根据提供的本地class路径转换为class对象 我们通过读取文件的字节并使用自定义的defineClass方法来加载Class对象。 在main方法中&#xff0c;我们创建了一个ConvertToClassObject对象并调用loadClassFromFile方法来加载Class对象。 这种方法直接读取本地文件的字节码来…

【单元测试】测试用例编写

在你想要了解如何编写单元测试的时候&#xff0c;想必对于单元测试的概念和重要性都已经有了比较充足的了解。 本篇不讲概念、不说废话&#xff0c;仅展示一些单元测试编写的技巧和方法&#xff0c;希望能够帮助大家实际用起来。 计划做一个单元测试的专栏&#xff0c;后续补充…

一文详解选择低代码开发平台的六大理由

在当今快节奏的数字时代&#xff0c;企业需要快速开发和部署应用程序以保持竞争力。传统编程方式耗时较长&#xff0c;且需要大量人力和物力投入。因此&#xff0c;低代码开发平台应运而生&#xff0c;它可以帮助企业快速构建应用程序&#xff0c;提高生产力。本文将为您阐述选…

oracle发送http请求

UTL_HTTP包让SQL和PLSQL能够调用超文本传输协议&#xff08;HTTP&#xff09;&#xff0c;也就是说可以使用它在Internet上访问数据。 当包用HTTPS从Web site获取数据时&#xff0c;要使用Oracle Wallet&#xff0c;它是由Oracle Wallet Manager或者orapki utility创建。非HTT…

Web应用安全测试-防护功能缺失

Web应用安全测试-防护功能缺失 1、Cookie属性问题 漏洞描述&#xff1a; Cookie属性缺乏相关的安全属性&#xff0c;如Secure属性、HttpOnly属性、Domain属性、Path属性、Expires属性等。 测试方法&#xff1a; 通过用web扫描工具进行对网站的扫描&#xff0c;如果存在相关…

TCP协议为啥会有三次挥手(close、shutdown)

一、前言 使用wireshrak抓包的时候&#xff0c;发现有的TCP断开连接&#xff0c;有的是三次挥手、有的是四次挥手&#xff0c;本文将带领带领大家一探究竟。 1. 四次挥手&#xff1a; 四次挥手流程回顾 第一次挥手&#xff1a; 主动关闭的一方&#xff08;客户端或服务器&…

算法安全自评估报告如何填写?(附模板)

之前&#xff0c;众森企服给大家讲过办理互联网信息服务算法备案有三部分组成&#xff1a;主体备案、算法备案和产品备案。 主体备案主要审查的就是一家主体公司是否有算法相应的规章制度&#xff0c;里面最主要的就是算法安全管理制度。 算法备案主要审查的就是算法本身的情…

【春秋云镜】Faculty Evaluation System未授权任意文件上传漏洞(CVE-2023-33440)

因为该靶场没有Write up,索性自己搞一下&#xff0c;方便别人&#xff0c;快乐自己&#xff01; 漏洞概述&#xff1a; Sourcecodester Faculty Evaluation System v1.0 is vulnerable to arbitrary code execution via /eval/ajax.php?actionsave_user. 漏洞复现&#xff…

simdjson 高性能JSON解析C++库

simdjson 是什么 simdjson 是一个用来解析JSON数据的 C 库&#xff0c;它使用常用的 SIMD 指令和微并行算法来每秒解析千兆字节的 JSON&#xff0c;在Velox, ClickHouse, Doris 中均有使用。 加载和解析 JSON documents 出于性能考虑&#xff0c;simdjson 需要一个末尾有几个…

2024年高考:计算机相关专业前景分析与选择建议

2024年高考结束&#xff0c;面对计算机专业是否仍具有吸引力的讨论&#xff0c;本文将从行业趋势、就业市场、个人兴趣与能力、专业选择建议等多个角度进行深入分析&#xff0c;以帮助考生和家长做出明智的决策。 文章目录 一、行业趋势与就业市场1. 计算机行业的发展与变革2. …

49.Chome浏览器有三种清缓存方式

49.Chome浏览器有三种清缓存方式&#xff1a;正常重新加载、硬件重新加载、清空缓存并硬性重新加载 1、【正常重新加载】 触发方式&#xff1a;①F5  ②CtrlR  ③在地址栏上回车  ④点击链接 如果缓存不过期会使用缓存。这样浏览器可以避免重新下载JavaScript文件、图像、…

LINUX 精通 3.2

main里怎么实现 accept_cb 里的regist部分抽出来 // regist拉出来 int event_register(int fd, int event) {if (fd < 0) return -1;conn_list[fd].fd fd;conn_list[fd].r_action.recv_callback recv_cb;conn_list[fd].send_callback send_cb;memset(conn_list[fd].rbuff…

【吉林大学Java程序设计】第8章:IO流

第8章&#xff1a;IO流 1.流与相关类1.1 流的概念1.2 File类1.3 字节流及其方法1.4 字符流及其方法1.5 其他IO流&#xff08;1&#xff09;节点流&#xff08;2&#xff09;处理流&#xff08;过滤流&#xff09;&#xff08;3&#xff09;文件流基于字节的文件流基于节符的文件…

Shell脚本(.sh文件)如何执行完毕之后不自动关闭?

Shell脚本异常傲娇&#xff0c;出错后、执行完根本不给你机会让你查看报错信息、输出信息&#xff0c;直接闪退。 废话不多说&#xff0c;调教方法如下&#xff0c;直接在Shell脚本末尾加上如下代码&#xff1a; 1、实现方式一 1.1 使用read命令达到类似bat中的pause命令效果…

深度解析响应式异步编程模型

上一篇文章中我们聊了一下线程池,基于线程池的多线程编程是我们在高并发场景下提升系统处理效率的有效手段,但却不是唯一的。今天我们来看一下另一种异步开发的常用手段-响应式编程模型 传统多线程模型的缺陷 多线程模型是目前应用最为广泛的并发编程手段,但凡遇到什么性能…

语义分割——mmsegmentation框架使用

目录 1.mmsegmentation简介 2.mmsegmentation安装 3.mmsegmentation使用&#xff08;代码结构介绍&#xff09; 4.mmsegmentation使用实战&#xff08;deeplab v3为例&#xff09; 4.1配置 4.2训练&#xff1a; 4.3预测&#xff1a; 1.mmsegmentation简介 mmsegmentatio…

LogicFlow 学习笔记——11. 对齐线 和 键盘快捷键

对齐线 Snapline 对齐线能够在节点移动过程中&#xff0c;将移动节点的位置与画布中其他节点位置进行对比&#xff0c;辅助位置调整。位置对比有如下两个方面。 节点中心位置节点的边框 对齐线使用 普通编辑模式下&#xff0c;默认开启对齐线&#xff0c;也可通过配置进行关…

自己想要公开自己的学习方法,但是自己很害怕自己的学习方法是一个错误的,因为对于自己而言,专升本的机会只有一次

分享自己的学习方法可能需要一定的勇气&#xff0c;特别是当你担心可能会受到批评或是不被理解时。以下是一些建议&#xff0c;可以帮助你克服这种恐惧&#xff1a;&#xff08;kimi编辑器自己对于这些内容的基础批注&#xff09; 自我肯定&#xff1a;首先&#xff0c;认识到你…