C# 反射(Reflection)总结

news2024/11/24 6:42:28

目录

什么是反射?

为什么使用反射?

反射机制的优缺点

如何使用反射?

一,Type访问元数据

获取/修改类中公有成员(属性PropertyI和字段Field等)

调用类中的公有构造函数Constructor

调用类中的公有方法Method

 获取类中的私有成员(包括方法)

二,Activator快速实例化对象

三,Assembly加载程序集


什么是反射?

反射是.NET Framework 的功能,并不是C#语言特有的功能。简单理解来看就是:

给我一个对象,我能在不用new操作符的情况下,也不知道是什么静态类型的情况下,创建一个同类型的对象,同时还能访问这个对象的各个成员。(在获取其他实体类的字段名或实列,只能获取公有的,而有了反射之后可以获取私有的,可以获取他的基类等等,可以说把家底查得清清楚楚。)

为什么使用反射?

很多时候程序的逻辑并不是在写的时候就能确定,有时需要用户交互时才确定,但此时程序已经运行。如果要程序员在静态编写时,去枚举用户可能做的操作,会让程序变得十分臃肿,可读性、可维护性都很烂,并且枚举用户可能做的操作这件事是很难实现的。这是我们需要的这种:以不变应万变的能力,就是反射机制。

通过反射在运行时取得一些编译期不存在的类型的信息,创建实例,或者调用方法。

反射机制的优缺点

优点

  • 1、反射提高了程序的灵活性和扩展性。
  • 2、降低耦合性,提高自适应能力。
  • 3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。

缺点

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

如何使用反射?

通过反射可以动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性,反射的语法主要通过三个类来实现:分别是Type、Assembly和Activator。

首先创建一个测试类Students方便测试:

    class Students
    {
        public string Name { get; set; }
        public int Age { get; set; }
        //公有字段
        public int a = 0;
        //私有属性
        private int weight { get; set; }
        public Students(string name,int age)
        {
            this.Name = name;
            this.Age = age;
        }
        public void message()
        {
            Console.WriteLine($"同学:{this.Name}已注册");
        }
    }

一,Type访问元数据

Type是类的信息类,它是反射功能的挤出,访问元数据的主要方式。
使用Type的成员获取有关类型(如构造函数、方法、字段、属性和类的事件等)声明的信息,具体步骤如下:

(1)获取Type,得到Students类的程序集信息,获取Type对象有两种方法:

//第一种:类未实例化(没用对象)
Type type = typeof(Students);

//第二种:类已实例化(new构建对象)
Students s= new Students();
Type studentsType = s.GetType();

 (2)获取Students类中的所有的信息

  • 获取/修改类中公有成员(属性PropertyI和字段Field等)

首先变量类型声明方式创建一个 Type 类型的变量 type,表示 Students类型。接着,使用反射 API 获取 Student 类型的所有公共成员(属性和字段(变量))等信息,使用 foreach 遍历每个成员信息对象。

由于属性信息(PropertyInfo)和字段信息(FieldInfo)的基类都是 MemberInfo,因此可以通过  GetValue(Object obj)SetValue(Object obj,Object value)对单个公有成员进行获取/设置。

            Students student = new Students("李华",23);
            Type type = student.GetType();
            //获取属性
            {
                //方式一(获取类中全部属性)
                PropertyInfo[] studentType = type.GetProperties();
                //方式二(获取类中指定属性)
                var property = type.GetProperty("Age");
                foreach (var stu in studentType)
                {
                    //获取属性值
                    object obj = stu.GetValue(student);
                    Console.WriteLine($"{obj}");
                }
                //修改Age属性值为50
                property.SetValue(student, 50);
                Console.WriteLine($"修改后Age的值:{property.GetValue(student)}");
            }
            //获取字段(修改字段值和上面写法一致)
            {
                FieldInfo[] fieldInfo = type.GetFields();
                //获取变量a的类型
                var filed = type.GetField("a");
            }

获取其他公有成员变量大同小异,这里只提示方法并不一一赘述。
1、得到枚举:GetEnumName、GetEnumNames
2、得到事件:GetEvent、GetEvents
3、得到接口:GetInterface、GetInterfaces

  • 调用类中的公有构造函数Constructor

获取类中的公有构造函数可以通过GetConstructor和GetConstructors获得,通过ConstructorInfo接收(或使用var类型),通过Invoke函数实例化构造函数。最后通过实例化对象可间接调用对象的方法以及对象的成员信息。

            //获取构造函数
            {
                //(一)获取无参构造函数
                ConstructorInfo info = type.GetConstructor(new Type[0]);
                //执行无参构造 无参构造没有参数传null
                Students obj = info.Invoke(null) as Students;
                //调用对象的方法
                obj.message();
                //(二)获取有参数构造函数
                ConstructorInfo info2 = type.GetConstructor(new Type[] { typeof(string), typeof(int) });
                //执行有参构造函数
                obj = info2.Invoke(new object[] { "小红", 11 }) as Students;
                obj.message();
            }
  • 调用类中的公有方法Method

获取成员方法可以通过Type类中的GetMethod方法来得到类中的方法,MethodInfo是方法的反射信息。最后通过Invoke函数进行调用如果存在方法重载,用Type数组表示参数类型。

需要注意:当调用的是静态方法时,Invoke函数的第一个参数为null。

                var method = type.GetMethods();
                foreach (var item in method)
                {
                    //Invoke(object obj,object value)
                    item.Invoke(student,null);
                }
                //打印结果:
                //同学:李华已注册
  •  获取类中的私有成员(包括方法)

反射最牛的地方就是它可以获取私有方法与实列:

(1)BindingFlags.Instance 表示只获取实例方法

(2)BindingFlags.NonPublic 表示只获取非公共方法

(3)BindingFlags.Public 表示只获取公共方法

将测试类Students的成员变量修改为私有属性(private),再通过反射获取类中的私有成员时,需要设置获取成员API的BindingFlags参数,以上文获取类中属性为例:

           Students student = new Students("李华",23);
            Type type = student.GetType();
            //获取属性
            {
                //方式一(获取类中全部属性)
                PropertyInfo[] studentType = type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic);
                //方式二(获取类中指定属性)
                var property = type.GetProperty("Age",BindingFlags.Instance | BindingFlags.NonPublic);
                foreach (var stu in studentType)
                {
                    //获取属性值
                    object obj = stu.GetValue(student);
                    Console.WriteLine($"{obj}");
                }
                //修改Age属性值为50
                property.SetValue(student, 50);
                Console.WriteLine($"修改后Age的值:{property.GetValue(student)}");
            }

二,Activator快速实例化对象

Activator用于实例化对象的类,通过type获取类型后,可以通过它实例化对象。Activator.CreateInstance默认调用无参构造函数。

1、无参构造函数:

            Type stu= typeof(Students);
            //快速实例化Students对象testObj
            Test testObj = Activator.CreateInstance(stu) as Students;

2、有参构造函数:

如果要调用有参构造函数,在后面一次添加参数即可。

            testObj = Activator.CreateInstance("小黄", 29) as Students;

三,Assembly加载程序集

Assembly类其实就是程序集类。主要用来加载其他程序集,加载后才能用Type来使用其他程序集中的信息,如果想要使用不是自己程序集中的内容,需要先加载程序集(比如dll文件)。

三种加载程序集的函数:

▲一般用来加载本地目录下的其他程序集

Assembly assembly = Assembly.Load(“程序集名称”);

▲一般用来加载不在本地目录下的其他程序集

Assembly assembly = Assembly.LoadFrom(“包含程序集清单的文件的名称或路径”);
Assembly assembly = Assembly.LoadFile(“要加载的文件的完全限定路径”);

使用方法:

1、首先加载一个指定程序集:

            Assembly assembly = Assembly.LoadFrom (@"C:\Users\TestDLL.dll");

2、再加载程序集中的一个类对象,之后才能使用反射

            //得到dll中的Class1类
            Type c = assembly.GetType("TestDLL.Class1");
            object o = Activator.CreateInstance(c);
            //调用Class1类中的Speak方法
            MethodInfo speak = c.GetMethod("Speak");
            speak.Invoke(o,null);

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

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

相关文章

【软件工程】软件工程期末考试复习题

填空题(每空1分,共25分) 软件生存周期一般可以划分为,问题定义、可行性研究、需求分析、设计、编码、测试和运行和维护。基于软件的功能划分,软件可以划分成___系统软件_、支撑软件、应用软件__三种。可行性研究&…

【UE 从零开始制作坦克】10-炮弹溅射伤害

目录 一、解决炮弹穿过坦克炮塔问题 二、炮弹溅射伤害 效果 一、解决炮弹穿过坦克炮塔问题 打开“PHYS_West_Tank_M1A1Abrams”这个物理资产 造成这种现象的原因是,炮弹只会与如下紫色区域产生碰撞事件 选中坦克炮塔的骨骼 添加盒体外形 缩放盒体外形使其包裹住…

数据库原理之数据库事物

文章目录 一、事物介绍1.1 事物的目的是保证数据的一致性1.2 事物的ACID A、I、D是为了实现 C1.3 什么是本地事物(Local Transactions) 二、数据库系统如何实现ACID2.1 影响深远的ARIES理论2.2 本地事物如何实现原子性和持久性 A、D2.2.1 实现原子性和持久性的Commit Logging方…

基于高精度三维机器视觉的汽车曲轴无序抓取系统应用

Part.1 行业背景 汽车产业的高速发展,对零部件自动化生产提出了更高要求。随着汽车销量的水涨船高,传统的手工生产模式已经难以满足大批量生产的需求,自动化生产是必然趋势。 曲轴是汽车发动机的关键组件之一,生产过程复杂&#…

【MySQL】如何速通MySQL(4)

📌前言:本篇博客介绍如何速通MySQL的第四篇,主要介绍Mysql中主要的基础的入门,学习MySQL之前要先安装好MySQL,如果还没有安装的小伙伴可以看看博主前面的博客,里面有详细的安装教程。或者看一下下面这个链接…

Linux(centos )防火墙常见操作

1、查看防火墙当前状态 systemctl status firewalld 2、开启防火墙 systemctl start firewalld 3、关闭防火墙 systemctl stop firewalld.service 4、如果报错:-bash: firewall-cmd: command not found,可能是没有安装 firewall。安装命令&#xff1a…

处理错误 Xcode 编译找不到文件 libarclite_iphonesimulator.a

处理错误 Xcode 编译找不到文件 libarclite_iphonesimulator.a 视频 https://youtu.be/ZBMFs2PwkB4 错误描述 Error (Xcode): File not found: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphonesimulator.aEr…

双非硕士-美国联培-马普所博士后-985系主任的逆袭之路

本科和硕士都是双非学校,博士期间曾有美国联合培养经历,毕业后到德国马普所从事博士后研究。现任985高校特聘教授、博/硕士生导师,系主任。知识人网小编特刊介绍李志明博士的逆袭之路。 随着国内就业压力的增大,高校招聘教师也呈现…

element 设置 table中的按钮权限

子组件 <template><!-- 二次封装表格&#xff0c; 仅支持单选 :style"{ height: height }"--><div class"self_table"><el-table:data"tableData"style"width: 100%"v-loading"loading"stripeselecti…

[游戏开发]Unity随机网格中空位置_二叉树

[目录] 0. 前言1. 简单随机2. 可用位置内随机3. 二叉树权重随机&#xff08;1&#xff09;分区域随机&#xff08;2&#xff09;设置权重均衡概率&#xff08;3&#xff09;二叉树缓存权重&#xff08;4&#xff09;利用二叉树随机&#xff08;5&#xff09;优缺点 4. 测试对比…

简要介绍 | Backbone与Baseline的区别

注1&#xff1a;本文系“简要介绍”系列之一&#xff0c;仅从概念上对Backbone和Baseline进行非常简要的介绍&#xff0c;不适合用于深入和详细的了解。 Backbone与Baseline的区别&#xff1a;从神经网络到性能基准 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来…

大厂测试员是如何编写测试用例呢?

一、测试用例是软件测试的核心 软件测试的重要性是毋庸置疑的。但如何以最少的人力、资源投入&#xff0c;在最短的时间内完成测试&#xff0c;发现软件系统的缺陷&#xff0c;保证软件的优良品质&#xff0c;则是软件公司探索和追求的目标。每个软件产品或软件开发项目都需要…

【活动总结】0617COC深圳社区首场线下AI技术沙龙活动最强总结

文章目录 活动的发起活动的宣传活动的进行活动的收尾活动的总结活动的致谢更多展望友情链接 就在2023年6月17日&#xff0c;CSDN COC 深圳城市开发者社区&#xff0c;在深圳大学组织了一次以【智能未来 —— 人工智能与城乡规划的交叉对话】为主题的线下沙龙活动&#xff0c;活…

基于spring boot的JsonSerializer 业务内容国际化

说起国际化&#xff0c;真的是老生常谈了。后端有各种i18n的依赖组件&#xff0c;springboot本身也支持i18n的设置&#xff0c;前端vue也有i18n的设置&#xff0c;这些常规操作就不提了&#xff0c;大家可以去搜索其他博客&#xff0c;写的都很详细。 本篇博客主要写的是业务内…

5大技巧,实现视频号预约直播人数暴涨!

两个体量相当的视频号&#xff0c;为什么别人的直播间人数过万&#xff0c;而自己的直播间却寥寥无几&#xff1f;这其中有一个非常重要的原因&#xff0c;就是预约直播的工作没有做好。 通常情况下&#xff0c;视频号直播预约人数和最终场观呈现1:10的比例&#xff0c;换言之…

聊聊Redis中的跳跃表

Redis 大家项目中应该都用过&#xff0c;哪怕没有分布式锁、幂等校验的一些逻辑使用场景&#xff0c;缓存数据这个大家肯定都用过吧&#xff1f;最简单的key-value格式&#xff0c;直接存储String类型。 当然&#xff0c;针对越来越复杂的业务场景&#xff0c;后续也可能用到li…

合宙Air724UG Cat.1模块硬件设计指南--数字语音接口

数字语音接口 简介 数字音频接口DAI&#xff0c;即Digital Audio Interfaces&#xff0c;表示在板级或板间传输数字音频信号的方式。相比于模拟接口&#xff0c;数字音频接口抗干扰能力更强&#xff0c;硬件设计简单&#xff0c;DAI在音频电路设计中得到越来越广泛的应用。 特…

【学习日记2023.6.20】之 分布式事务_CAP定理_BASE理论_微服务集成Seata_Seata的四种事务模式_高可用架构模型

文章目录 1. 分布式事务问题1.1 本地事务1.2 分布式事务1.3 演示分布式事务问题 2. 理论基础2.1 CAP定理2.1.1 一致性2.1.2 可用性2.1.3 分区容错2.1.4 矛盾 2.2 BASE理论2.3 解决分布式事务的思路 3. 初识Seata3.1 Seata的架构3.2 部署TC服务3.2.1 下载3.2.2 解压3.2.3 修改配…

数据库系统概述——第七章 数据库设计(知识点复习+练习题)

&#x1f31f;博主&#xff1a;命运之光 &#x1f984;专栏&#xff1a;离散数学考前复习&#xff08;知识点题&#xff09; &#x1f353;专栏&#xff1a;概率论期末速成&#xff08;一套卷&#xff09; &#x1f433;专栏&#xff1a;数字电路考前复习 &#x1f99a;专栏&am…

p7付费课程笔记:jvm基础知识、字节码、类加载器

编程语言 演化&#xff1a; 机器语言->编程语言->高级语言&#xff08;java&#xff0c;c,Go,Rust等&#xff09; 面向过程–面向对象-面向函数 java是一种面向对象、静态类型、编译执行&#xff0c;有VM&#xff08;虚拟机&#xff09;/GC和运行时、跨平台的高级语言…