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的属性值并加10
                            int 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.mfbz.cn/a/716104.html

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

相关文章

DAY5-力扣刷题

1.两两交换链表中的节点 24. 两两交换链表中的节点 - 力扣&#xff08;LeetCode&#xff09; 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换…

Kubernetes新手必看:快速生成YAML清单的终极指南!

在这篇文章中&#xff0c;你将学习到几种快速创建Kubernetes YAML清单的方法&#xff0c;这些方法可以帮助你在Kubernetes中测试和部署应用程序。这些技巧同样适用于Kubernetes认证考试。 在使用Kubernetes时&#xff0c;我们经常需要搜索Kubernetes YAML文件以便部署测试Pod、…

编写一个简单的Mybatis插件

1.编写一个类&#xff0c;实现Intercepter这个接口 2.完成这个类的方法&#xff0c;并通过注解Intercepts来告诉Mybatis这个插件拦截哪个类和哪个方法 3.在Mybatis的全局配置文件里注册这个插件&#xff0c;让插件生效 4.玩一个实际功能的插件

家庭智能助手:Kompas AI引领家居智能化新纪元

一、引言 在数字化浪潮的推动下&#xff0c;现代家庭生活正迅速向智能化转型。从简单的自动化设备到复杂的智能家居系统&#xff0c;智能技术正悄无声息地改变我们的日常生活。Kompas AI作为一款前沿的家庭智能助手&#xff0c;不仅预示着家庭生活的未来趋势&#xff0c;更以其…

每日一练:攻防世界:ewm

这道题我尝试了使用montagegaps解题&#xff0c;但是没有解出来&#xff0c;图片数量不是很多&#xff0c;可以尝试用PS直接拼图&#xff0c;但是这样学不到东西&#xff0c;我也就没尝试&#xff0c;直接看的官方WP 这段代码应该是改变工作目录到small&#xff0c;并且变量当…

Sping源码(九)—— Bean的初始化(非懒加载)— Bean的创建方式(自定义BeanPostProcessor)

序言 之前文章有介绍采用FactoryBean的方式创建对象&#xff0c;以及使用反射创建对象。 这篇文章继续介绍Spring中创建Bean的形式之一——自定义BeanPostProcessor。 之前在介绍BeanPostProcessor的文章中有提到&#xff0c;BeanPostProcessor接口的实现中有一个Instantiatio…

Tensorflow-GPU工具包了解和详细安装方法

目录 基础知识信息了解 显卡算力 CUDA兼容 Tensorflow gpu安装 CUDA/cuDNN匹配和下载 查看Conda driver的版本 下载CUDA工具包 查看对应cuDNN版本 下载cuDNN加速库 CUDA/cuDNN安装 CUDA安装方法 cuDNN加速库安装 配置CUDA/cuDNN环境变量 配置环境变量 核验是否安…

【乐吾乐2D可视化组态编辑器】导航

支持点击图元&#xff0c;切换画面或跳转链接。 乐吾乐2D可视化组态编辑器地址&#xff1a;https://2d.le5le.com/ 切换画面 1. 添加事件 2. 设置事件行为 事件行为"发送消息"&#xff0c;消息名选择"导航"。 3. 配置消息参数 消息参数&#xff0c;…

图像的对比度和亮度

目标 访问像素值用0来初始化矩阵cv::saturate_cast像素转换提高一张图像的亮度 原理 图像处理 图像变换可以被视作两个步骤&#xff1a; 点操纵&#xff08;像素转换&#xff09;相邻区域转换&#xff08;以面积为基础&#xff09; 像素转换 在这种图像处理的转换过程中…

操作系统 - 进程的控制(创建与终止)

进程控制 文章目录 进程控制进程创建进程的终止wait()和waitpd()僵尸进程孤儿进程 进程创建 程序在执行的过程中&#xff0c;可能会创建多个进程&#xff0c;创建进程称为父进程&#xff0c;新的进程称为子进程&#xff0c;每个新的进程也可以创建其他进程&#xff0c;从而形成…

LinkedHashMap详解

目录 LinkedHashMap详解1、LinkedHashMap的继承体系2、LinkedHashMap的特性介绍和代码示例①、特性②、适用场景使用LinkedHashMap 实现最简单的 LRU缓存 3、LinkedHashMap的构造函数4、LinkedHashMap是如何存储元素的&#xff0c;底层数据结构是什么&#xff1f;LinkedHashMap…

功能强大的多功能文档转换工具Neevia Document Converter Pro 7.5.0.241

Neevia Document Converter Pro是一款功能强大的Windows软件,旨在将文档转换为各种格式,包括PDF、TIFF、JPEG和许多其他格式。该程序专为在企业环境中使用而设计,提供文档转换和处理过程的自动化,这使其成为处理大量文档的组织的***工具。 Neevia Document Converter Pro的…

基于Quartus Prime18.1的安装与FPGA的基础仿真(联合Modelsim)教程

Quartus是一种美国科技公司Intel&#xff08;英特尔&#xff09;公司开发的FPGA&#xff08;现场可编辑门阵列&#xff09;设计编译软件&#xff0c;用作设计、仿真、综合和布局、支持多种编程语言&#xff0c;包括VHDL、Verilog等&#xff0c;并具有丰富的功能和工具库&#x…

游戏中插入音效

一、背景音乐 准备&#xff1a;素材音乐 方法&#xff1a; 1、方法1&#xff1a; (1) 将背景音乐 bgAudio 拖放到Hierarchy面板 (2) 选中 bgAudio&#xff0c;勾选开始运行就播放、循环播放。调节音量&#xff08;volume) 2、方法2&#xff1a; (1) Create Empty&#x…

日志通关(一)

转载&#xff1a;https://mp.weixin.qq.com/s/eIiu08fVk194E0BgGL5gow 一、 日志体系 日志发展到今天&#xff0c;被抽象成了三层&#xff1a;接口层、实现层、适配层&#xff1a; 接口层&#xff1a;或者叫日志门面&#xff08;facade&#xff09;&#xff0c;就是interfa…

Aspice介绍——测试流程

文章目录 ASPICE简介一、V字模型的示意二、测试领域2.1 SWE.6&#xff1a;软件合格性测试过程目的过程成果基本实践&#xff08;BP&#xff09; 2.2 SYS.4:系统集成和集成测试过程目的过程成果基本实践&#xff08;BP&#xff09; 2.3 SYS.5&#xff1a;系统合格性测试过程目的…

【linux网络(四)】传输层协议详解(上)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Linux从入门到精通⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学更多操作系统知识   &#x1f51d;&#x1f51d; Linux网络 1. 前言2. UDP协议…

备忘录怎么插入文件和附件 备忘录插入文件附件方法

在繁忙的工作与生活中&#xff0c;我们时常需要记录各种信息&#xff0c;而备忘录则成为了我们不可或缺的得力助手。然而&#xff0c;当备忘录中需要包含的文件或附件越来越多时&#xff0c;如何高效、便捷地管理这些文件&#xff0c;便成为了一个亟待解决的问题。 想象一下&a…

深入剖析 Laravel 框架:构建高效PHP应用的最佳实践

引言 随着互联网的高速发展&#xff0c;PHP 作为一门广泛使用的服务器端脚本语言&#xff0c;始终备受开发者青睐。而在众多 PHP 框架中&#xff0c;Laravel 凭借其优雅的设计和高效率&#xff0c;成为了构建现代 Web 应用的热门选择。本文将从零开始&#xff0c;探讨如何使用 …

arcgis portal安装教程(含ECP授权文件)

本文介绍Portal 在windows环境下的安装部署过程&#xff0c;为了顺利进行Portal的安装&#xff0c;建议安装环境是windows server 2016。所以在操作之前首先保证有符合条件的安装机器或虚拟机&#xff0c;安装环境的存储空间建议不低于100G。 安装环境及软件 1、环境&#xff…