C#进阶学习(十四)反射的概念以及关键类Type

目录

本文末尾有相关类中的总结,如有需要直接跳到最后即可

前置知识:

1、程序集(Assembly)

2、元数据(Metadata)

[3、中间语言(IL, Intermediate Language)](#3、中间语言(IL, Intermediate Language))

中间语言(IL)是.NET编译器(如C#、VB.NET编译器)生成的平台无关代码。它介于高级语言(如C#)和机器码之间,是.NET程序集的执行逻辑核心。

特点

生命周期:

[4、 资源(Resources)](#4、 资源(Resources))

定义:资源是程序集中嵌入的非代码数据,例如图像、音频、字符串、本地化文件(多语言文本)、配置文件等。它们与代码逻辑分离,便于动态管理和复用。

类型

作用

访问方式

5、清单(Manifest)

[定义:清单是程序集的元数据,描述了程序集自身的身份信息、依赖关系、安全权限等。它是程序集的"自述文件",确保 CLR 能正确加载和执行程序集。](#定义:清单是程序集的元数据,描述了程序集自身的身份信息、依赖关系、安全权限等。它是程序集的“自述文件”,确保 CLR 能正确加载和执行程序集。)

内容

(1)程序集标识:

(2)依赖项:

(3)文件列表:

(4)安全权限:

作用

示例

一、什么是反射

具体表现:

核心原理

二、补充:平常写完代码执行和动态操作有什么区别?

常规代码执行(静态绑定):

反射动态操作(动态绑定):

核心区别:

二、反射的作用的场景

[1. 插件系统与动态扩展](#1. 插件系统与动态扩展)

[2. 依赖注入与框架开发](#2. 依赖注入与框架开发)

[3. 序列化与反序列化](#3. 序列化与反序列化)

反射存在的必要性?

三、和反射相关的重要类和语法

[1. Type 类](#1. Type 类)

[(1)获取 Type 对象的三种方式](#(1)获取 Type 对象的三种方式)

1)typeof 运算符直接通过类型名称获取:typeof 运算符直接通过类型名称获取:)

2)实例的 GetType() 方法通过对象实例获取:实例的 GetType() 方法通过对象实例获取:)

3)Type.GetType() 静态方法通过类型名称字符串(需完整命名空间)获取:注意类名必须包含命名空间 不然找不到Type.GetType() 静态方法通过类型名称字符串(需完整命名空间)获取:注意类名必须包含命名空间 不然找不到)

[(2)Type 类的重要方法和属性](#(2)Type 类的重要方法和属性)

1)获取某一个类的公共成员

2)获取某一个类公共构造函数并调用

①无参构造函数的调用

步骤:

[② 有参构造函数的调用](#② 有参构造函数的调用)

步骤:

[③ 处理多个构造函数的情况](#③ 处理多个构造函数的情况)

3)获取某一个类公共成员变量

GetFields()获取所有公共字段:

获取指定名称的公共字段

获取和设置指定字段的值:

4)获取某一个类公共方法

[GetMethods()获取所有公共方法: MethodInfo 是方法的反射信息](#GetMethods()获取所有公共方法: MethodInfo 是方法的反射信息)

①基本调用:实例方法

②处理重载方法:就是什么类型传什么参数

③调用静态方法

5)枚举操作

6)事件操作

7)属性操作

8)接口操作

[2. Assembly 类](#2. Assembly 类)

(1)加载程序集的方式

对比总结

(2)获取程序集中的类型

(3)访问清单信息

[3. Activator 类](#3. Activator 类)

(1)创建无参实例

语法:

注意事项

(2)创建带参实例

示例:

注意事项

(3)处理私有构造函数

语法:

反射核心类总结表


引言:

在.NET生态中,反射(Reflection) 是一种突破静态语言限制动态编程机制 ,赋予程序在运行时"自省"与"操控"代码的能力。通过反射,开发者无需在编译时绑定具体类型,即可动态加载程序集、解析类型信息、创建对象实例及调用方法 。这种能力为插件系统、依赖注入框架、序列化工具等复杂场景提供了核心支持。例如,主程序可通过反射动态加载未知插件 ,ORM框架能自动映射数据库字段到对象属性,依赖注入容器可解析服务间的复杂依赖链。然而,反射的灵活性也伴随性能开销和复杂性挑战。理解反射的核心原理、关键类(如TypeAssembly)及适用场景,是掌握现代.NET高级开发的关键。本文将从程序集、元数据等基础概念出发,系统剖析反射的实现机制,并通过实战示例展示其强大能力与使用边界。

本文末尾有反射相关类中的总结,如有需要直接跳到最后即可

前置知识:

1、程序集(Assembly)

程序集时经由编译器编译生成的可执行文件 供进一步编译执行的那个中间产物
在Windows系统中他一般表现为.exe(可执行文件)或者.dll文件(库文件)

说人话:
程序集就是我们写的一个代码集合 我们现在写的所有代码
最终都会被编译成程序集供别人使用
比如一个代码库文件.dll或者 一个可执行文件.exe

定义 :程序集是.NET应用程序的基本构建块,是编译后的代码逻辑单元。它可以是一个可执行文件(.exe)或动态链接库(.dll)。

可以在这里看:

等会我们也可以通过代码进行查看

组成

中间语言(IL, Intermediate Language):由编译器生成的与平台无关的代码。

元数据(Metadata):描述程序集中包含的类型、方法、属性等信息。

资源(Resources):如图像、字符串、本地化文件等。

清单(Manifest):程序集的元数据,包括版本、依赖项、安全权限等。

作用

是.NET应用程序部署和版本控制的基本单元。

支持代码共享和重用。

2、元数据(Metadata)

定义:元数据是描述程序结构和内容的数据。在.NET中,元数据记录了程序集中的类型(类、接口、结构体)、成员(方法、属性、字段)、依赖关系等信息。

内容

类型名称、命名空间、可见性(public/private)。

方法的签名(参数、返回值)。

属性的类型和访问级别。

继承关系(基类、接口实现)。

作用

支持反射机制。

确保类型安全(CLR通过元数据验证代码)。

为调试工具、IDE(如Visual Studio)提供智能感知功能。

3、中间语言(IL, Intermediate Language)

定义

中间语言(IL)是.NET编译器(如C#、VB.NET编译器)生成的平台无关代码。它介于高级语言(如C#)和机器码之间,是.NET程序集的执行逻辑核心。
特点

平台无关性:IL 不依赖具体硬件或操作系统,可在任何支持 .NET 运行时(CLR)的环境中运行。

面向堆栈:IL 是基于堆栈的指令集(类似汇编语言),操作通过压栈和弹栈完成。

安全性:CLR 在运行前会验证 IL 代码的安全性(如类型安全、内存安全)。

生命周期:
  1. 编译 :C# 代码 → 编译器生成 IL(保存在 .dll.exe 文件中)。

  2. 执行 :CLR 通过 JIT 编译器(Just-In-Time Compiler) 将 IL 转换为当前平台的本地机器码(Native Code)。

C# → IL → JIT编译 → 机器码 → CPU执行

4、 资源(Resources)

定义:资源是程序集中嵌入的非代码数据,例如图像、音频、字符串、本地化文件(多语言文本)、配置文件等。它们与代码逻辑分离,便于动态管理和复用。
类型

嵌入式资源(Embedded Resource):直接编译到程序集中,通过代码访问。

链接资源(Linked Resource):在程序集中记录资源路径,但文件独立存在。

作用

本地化:存储不同语言的字符串,支持应用程序国际化。

静态内容管理:如图标、默认配置文件。

动态加载:运行时按需加载资源(如插件系统中的图片)。

访问方式

在 C# 中通过 ResourceManager 类访问资源:

cs 复制代码
// 假设资源文件名为 Resources.resx
ResourceManager rm = new ResourceManager("MyApp.Resources", Assembly.GetExecutingAssembly());
string greeting = rm.GetString("Greeting"); // 获取键为 "Greeting" 的字符串

5、清单(Manifest)

定义:清单是程序集的元数据,描述了程序集自身的身份信息、依赖关系、安全权限等。它是程序集的"自述文件",确保 CLR 能正确加载和执行程序集。
内容
(1)程序集标识

名称(Name)

版本号(Version,格式:主版本.次版本.生成号.修订号)

文化信息(Culture,用于本地化资源)。

公钥令牌(Public Key Token,用于强名称程序集)。

(2)依赖项

引用的其他程序集(名称、版本、公钥)。

(3)文件列表

程序集包含的所有文件(如主模块、资源文件)。

(4)安全权限

程序集运行所需的权限(如文件访问、网络访问)。

作用

版本控制:避免"DLL Hell"(同一 DLL 不同版本的冲突)。

依赖解析:CLR 根据清单查找并加载依赖项。

部署简化:自包含的元数据无需注册表(与 COM 不同)。

示例

一个程序集的清单可能包含:

XML 复制代码
<assembly>
  <name>MyLibrary</name>
  <version>1.0.0.0</version>
  <culture>neutral</culture>
  <publicKeyToken>a1b2c3d4e5f6g7h8</publicKeyToken>
  <dependency>
    <dependentAssembly>
      <name>Newtonsoft.Json</name>
      <version>13.0.0.0</version>
    </dependentAssembly>
  </dependency>
</assembly>

小结:

在.NET框架中,程序集(Assembly) 作为应用程序的基本构建单元 ,本质上是一个逻辑容器 ,整合了四个核心组成部分:中间语言(IL)代码元数据(Metadata)资源(Resources)清单(Manifest)。这些内容相互协作,共同支撑程序的编译、运行和动态行为。程序集中的IL代码是由高级语言(如C#)编译生成的平台无关指令 ,定义了程序的执行逻辑元数据则如同"代码的说明书" ,详细描述了IL中的类型(如类、接口)、成员(如方法、属性)及其依赖关系 ,确保运行时环境能够理解并安全执行代码。清单作为程序集 的"身份标识 ",记录了程序集的名称、版本、依赖项和安全权限等顶层信息 ,是CLR(公共语言运行时)加载和验证程序集的依据。资源则嵌入非代码数据(如图像、本地化文本),通过资源管理器动态加载,与代码逻辑解耦。

四者之间的关系紧密且功能互补。元数据不仅为IL代码中类型和方法 提供结构化描述 (例如方法的参数和返回类型),还指导JIT(即时)编译器将IL转换为机器码时进行类型验证和内存分配。清单作为元数据的扩展,专注于程序集级别的信息管理,例如声明依赖的外部程序集(如mscorlib.dll),使得CLR能够按需加载这些依赖项。资源文件虽然在逻辑上独立于代码,但其存在和路径信息通常通过清单记录,而IL代码则通过ResourceManager类在运行时动态访问 这些资源(例如加载多语言字符串)。在程序的生命周期 中,编译阶段将源代码转换为IL代码,同时生成元数据和清单,并将资源文件嵌入程序集;运行阶段,CLR首先读取清单以加载所有依赖项,接着利用元数据验证类型安全并指导JIT编译,最终通过反射机制或资源管理器实现动态类型操作和资源加载。这种分层协作机制不仅保障了.NET应用程序的跨平台性和安全性,还为高级功能(如反射、插件系统)提供了底层支持。




一、什么是反射

反射(Reflection)是 .NET 框架提供的一种动态编程机制,允许程序在运行时 (而非编译时)获取程序集、类型及其成员的信息 ,并能够动态创建对象、调用方法、访问属性或字段

简单来说,反射让代码可以像"镜子"一样,在运行时"照见"自身的结构,并基于此结构动态操作代码逻辑,而无需在编译时硬编码类型信息。

反射的核心意义在于赋予程序运行时动态操作代码的能力,使得代码不再被编译时的静态结构所束缚。通过反射,程序可以在不预先知晓类型细节的情况下,动态加载、解析和操作代码逻辑,从而应对复杂多变的场景需求。

"运行时动态操作代码的能力"指的是程序在运行阶段 (而非编写或编译时)能够动态地分析、修改或调用代码逻辑。这种能力允许程序在事先不知道具体类型或方法的情况下,根据运行时的条件(如配置文件、用户输入)灵活地执行操作。就比如你给你的浏览器装一个插件,但是你并没有关闭浏览器对吧,只是可能在让你装完之后重启一下,这就是动态的执行预先设定好的操作。

具体表现

动态加载程序集 :例如,一个主程序在运行时加载一个未知的插件(.dll 文件)。

动态创建对象 :根据字符串形式的类名(如 "MyApp.Logger")创建实例。

动态调用方法 :通过方法名称字符串(如 "Calculate")调用方法,而无需在代码中硬编码方法名。

访问私有成员:绕过编译时的访问限制,读取或修改类的私有字段。

核心原理

反射基于程序集中的**元数据(Metadata)**实现。元数据是嵌入在程序集中的结构化信息,记录了以下内容:

类型的定义(类、接口、结构体、枚举)。

类型的成员(方法、属性、字段、事件)。

方法的参数和返回值类型。

程序集的版本、依赖项等。

通过反射 API,程序可以读取这些元数据,并利用它们动态执行操作。例如,即使不知道某个类的具体定义,也能通过反射创建它的实例并调用其方法。

一个最简单的示例:

假设有一个插件系统,主程序需要加载不同开发者编写的插件:

cs 复制代码
// 主程序不知道具体的插件类名
string pluginName = ReadPluginNameFromConfig(); // 例如返回 "MyPlugin.Logger"
Assembly assembly = Assembly.LoadFrom(pluginName + ".dll");
Type pluginType = assembly.GetType(pluginName);
object plugin = Activator.CreateInstance(pluginType);

// 动态调用插件的 "Run" 方法
MethodInfo runMethod = pluginType.GetMethod("Run");
runMethod.Invoke(plugin, null);

这里,主程序在编译时完全不知道 MyPlugin.Logger 类的存在,而是通过反射在运行时动态加载和调用。

二、补充:平常写完代码执行和动态操作有什么区别?

常规代码执行(静态绑定)

特点 :代码在编译时已经确定了所有类型、方法和调用关系。

示例

cs 复制代码
// 直接调用已知类的方法
MyLogger logger = new MyLogger();
logger.Log("Hello"); // 编译时明确知道 MyLogger 类和 Log 方法

优势

性能高(编译器优化)。

代码可读性强(类型和方法名直接可见)。

限制

无法适应动态需求(如插件、动态配置)。

必须提前知道所有类型和依赖。

反射动态操作(动态绑定)

特点 :代码在运行时动态解析类型和调用方法。

示例

这里看不懂没关系,只是一个简单的示例而已,我们后面会好好讲里面的门道的。

cs 复制代码
// 通过反射动态调用方法
string className = "MyLogger";
string methodName = "Log";

Type type = Type.GetType(className);
object obj = Activator.CreateInstance(type);
MethodInfo method = type.GetMethod(methodName);
method.Invoke(obj, new object[] { "Hello" });

优势

灵活应对未知类型(如插件、动态配置)。

支持通用框架开发(如依赖注入、ORM)。

缺点

性能较低(需运行时解析元数据)。

代码复杂度高,可维护性差。

核心区别
维度 常规执行 反射动态操作
绑定时机 编译时确定类型和方法 运行时动态解析类型和方法
灵活性 低(依赖编译时已知类型) 高(适应未知类型和动态需求)
性能 高(编译器优化) 低(运行时解析开销)
使用场景 固定逻辑、核心业务代码 插件系统、框架、动态配置

小结:

运行时动态操作代码:在程序运行阶段动态解析和调用类型、方法,适应灵活需求。

与常规执行的区别:静态绑定在编译时固定,动态绑定在运行时解析,牺牲性能换取灵活性。

编译时的静态结构:代码在编译阶段确定的类型和成员定义,是元数据和 IL 的基础。

反射的核心价值在于弥合静态语言在动态场景下的不足,但其使用需权衡灵活性与性能。

二、反射的作用的场景

随便列举了几个可能的场景,来加深对反射的理解

1. 插件系统与动态扩展

场景:开发一个支持插件(如浏览器扩展、IDE插件)的应用程序,允许第三方开发者编写功能模块,主程序在运行时动态加载这些插件。

为什么需要反射

动态加载 :主程序无法预先知道插件的具体类型和实现,需通过反射加载插件程序集(.dll)。

接口解耦 :插件可能实现某个约定接口(如IPlugin),反射可以遍历程序集,找到所有实现该接口的类并实例化。

示例:Visual Studio 的扩展功能、游戏 Mod 系统。

2. 依赖注入与框架开发

场景:构建一个依赖注入(DI)框架(如 ASP.NET Core 的内置容器),自动解析服务类型并注入依赖对象。

为什么需要反射

类型解析:框架需要根据配置或约定(如构造函数参数)动态创建对象实例。

生命周期管理 :反射用于检查类型的依赖关系树,确保单例、瞬态等生命周期的正确实现。示例ASP.NET Core 的 Startup 类中通过反射扫描并注册服务。

3. 序列化与反序列化

场景 :将对象转换为 JSON/XML(如 Newtonsoft.Json),或从数据库读取数据填充到对象(ORM 框架)。

为什么需要反射

动态读写属性:序列化库需要遍历对象的所有属性(包括私有字段),反射可以绕过编译时的访问限制。

处理未知类型:反序列化时,根据 JSON 中的类型名称动态创建对象。

示例JsonConvert.SerializeObject() 内部使用反射遍历对象属性。

小结:

反射存在的必要性?

突破静态语言的限制

C#、Java 等静态语言在编译时要求类型必须明确,但实际开发中常需处理"未知类型"(如插件、动态配置)。反射填补了这一鸿沟,允许在运行时动态解析类型。

提升代码的灵活性与扩展性

反射使程序能够"动态适应变化"。例如,插件系统无需重新编译主程序即可扩展功能。框架开发者通过反射实现通用逻辑(如依赖注入、ORM),减少硬编码。

支持高级编程范式自省(Introspection)

程序可以检查自身结构,用于调试、序列化等场景。

应对复杂业务需求:

在需要高度动态行为的场景(如规则引擎、工作流系统),反射是实现灵活业务逻辑的核心技术。

元编程

反射允许代码操作代码本身,实现 AOP、动态代理等高级模式。

反射的缺点:

性能开销 :反射操作比直接调用慢数十倍,频繁调用需缓存 MethodInfo 等元数据。

代码可维护性:过度使用反射会导致代码晦涩难懂,增加调试难度。

安全性:反射可绕过访问限制,可能破坏封装性,需严格控制权限。

反射是 .NET 中实现动态编程 的基石,它的存在解决了静态语言在运行时动态操作代码的难题。无论是构建灵活可扩展的框架(如 ASP.NET Core、Entity Framework),还是开发插件化应用、高效测试工具,反射都发挥着不可替代的作用,因为反射可以在程序编译后获得信息,所以他提高了程序的拓展性和灵活性:

1.程序运行时得到所有的元数据 包括元数据的特性

2.程序运行时实例化对象 操作对象

3.程序运行时创建新的对象 用这些对象执行任务

然而,需在灵活性性能可维护性之间权衡,避免滥用反射导致代码质量下降。理解反射的适用场景与代价,是将其价值最大化的关键。

三、和反射相关的重要类和语法

好的,接下来咱们来学习如何进行反射的操作。下面操作需要引用命名空间 using System.Reflection

首先定义一个类来被用:

cs 复制代码
using System;

public class MyClass
{
    // 公共属性
    public string Message { get; set; }
    public int Value { get; }

    // 无参构造函数
    public MyClass()
    {
        Message = "默认消息";
        Console.WriteLine("无参构造函数被调用!");
    }

    // 有参构造函数
    public MyClass(string message, int value)
    {
        Message = message;
        Value = value;
        Console.WriteLine($"有参构造函数被调用!Message={message}, Value={value}");
    }

    // 公共方法
    public void PrintInfo()
    {
        Console.WriteLine($"Message: {Message}, Value: {Value}");
    }
}

1. Type

Type 类是反射的核心,用于表示类型(类、接口、结构体、枚举等),提供了访问类型元数据的所有方法。

Type(类的信息类)

他是反射功能的基础

他是访问元数据的主要方式

使用Type的成员获取有关类型申明的信息

有关类型的成员(如构造函数,方法,字段,属性和类的事件)

(1)获取 Type 对象的三种方式

1)typeof 运算符

直接通过类型名称获取:

cs 复制代码
Type type1 = typeof(MyClass);  // 静态类型
2)实例的 GetType() 方法

通过对象实例获取:

cs 复制代码
MyClass obj = new MyClass();
Type type2 = obj.GetType();
3)Type.GetType() 静态方法

通过类型名称字符串(需完整命名空间)获取:注意类名必须包含命名空间 不然找不到

cs 复制代码
Type type3 = Type.GetType("Namespace.MyClass");  // 需包含程序集信息
Type type4 = Type.GetType("System.Int32");

(2)Type 类的重要方法和属性

获取程序集信息:

Assembly 属性

获取类型所属的程序集:

cs 复制代码
Assembly assembly = typeof(MyClass).Assembly;
Console.WriteLine(assembly.FullName);  // 输出程序集名称和版本

//例如
Console.WriteLine(type.Assembly);
Console.WriteLine(type2.Assembly);
Console.WriteLine(type3.Assembly);
1)获取某一个类的公共成员
  • GetMembers()

    获取所有公共成员(包括方法、属性、字段等):一般没啥用,都是用什么获取什么

cs 复制代码
MemberInfo[] members = typeof(MyClass).GetMembers();
foreach (MemberInfo member in members) {
    Console.WriteLine(member.Name);
}
2)获取某一个类公共构造函数并调用
  • GetConstructors()

    获取所有公共构造函数:

cs 复制代码
ConstructorInfo[] constructors = typeof(MyClass).GetConstructors();
foreach (ConstructorInfo ctor in constructors) {
    Console.WriteLine($"参数个数: {ctor.GetParameters().Length}");
}

调用构造函数

既然获取到了这一个类中的构造函数,那我们肯定是要来使用的,咋个使用嘞。具体使用有无参构造函数请和有参构造函数的使用,请看下面:

①无参构造函数的调用
步骤

获取类型的 Type 对象

获取无参构造函数 :通过 GetConstructor 方法传入空参数类型数组。

调用构造函数 :使用 ConstructorInfo.Invoke 方法,参数为 null

请看代码:

cs 复制代码
using System;
using System.Reflection;

public class MyClass {
    public MyClass() {
        Console.WriteLine("无参构造函数被调用!");
    }
}

// 获取 Type 对象
Type type = typeof(MyClass);

// 获取无参构造函数(参数类型数组为空)
ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes);

// 调用构造函数创建实例
object instance = ctor.Invoke(null);  // 输出:无参构造函数被调用!
② 有参构造函数的调用
步骤

获取类型的 Type 对象

获取有参构造函数 :通过 GetConstructor 方法传入参数类型的 Type 数组。

调用构造函数 :使用 ConstructorInfo.Invoke 方法,传入实际参数值数组。

cs 复制代码
using System;
using System.Reflection;

public class Person {
    public string Name { get; }
    public int Age { get; }

    public Person(string name, int age) {
        Name = name;
        Age = age;
        Console.WriteLine($"有参构造函数被调用!Name={Name}, Age={Age}");
    }
}

// 获取 Type 对象
Type type = typeof(Person);

// 定义参数类型数组(string 和 int)
Type[] paramTypes = new Type[] { typeof(string), typeof(int) };

// 获取匹配的构造函数
ConstructorInfo ctor = type.GetConstructor(paramTypes);

// 准备实际参数值
object[] parameters = new object[] { "Alice", 30 };

// 调用构造函数创建实例
object instance = ctor.Invoke(parameters);  // 输出:有参构造函数被调用!Name=Alice, Age=30
③ 处理多个构造函数的情况

如果类型有多个构造函数(例如无参、有参),可以通过 GetConstructors 遍历所有构造函数,并选择需要的签名。

cs 复制代码
public class Calculator {
    public Calculator() {
        Console.WriteLine("无参构造函数");
    }

    public Calculator(int initialValue) {
        Console.WriteLine($"有参构造函数,初始值={initialValue}");
    }
}

// 获取所有公共构造函数
ConstructorInfo[] ctors = typeof(Calculator).GetConstructors();

foreach (ConstructorInfo ctor in ctors) {
    ParameterInfo[] parameters = ctor.GetParameters();
    Console.WriteLine($"构造函数参数个数: {parameters.Length}");
}

// 选择有参构造函数
ConstructorInfo intCtor = typeof(Calculator).GetConstructor(new Type[] { typeof(int) });
object calc = intCtor.Invoke(new object[] { 100 });  // 输出:有参构造函数,初始值=100

注意哈:这里我们都是使用的object万物之父进行装载的,你在实际使用时候要转回去对应的类型的。 还有一个值得注意的是,这里的类我们都是直接给出来了,一般情况是我们是要获取到别的程序集中的类的,这里只是先讲解Type里面的内容,后面我们再讲解从别的程序集中获取对应类信息的。

嘿!?写完了这么多代码,才将获取到的类对象实例化出来了,是不是有点心累啊,别急,我们后面会学习更加高效的方式进行类对象的实例化。

小小结:

关键点总结

  1. 获取构造函数

    无参构造:GetConstructor(Type.EmptyTypes)。有参构造:GetConstructor(new Type[] { typeof(string), ... })

  2. 调用构造函数ConstructorInfo.Invoke(object[] parameters)。参数必须与构造函数签名严格匹配,否则抛出 TargetParameterCountException

  3. 性能优化 :缓存 ConstructorInfo 对象避免重复反射。

3)获取某一个类公共成员变量
GetFields()

获取所有公共字段:

cs 复制代码
FieldInfo[] fields = typeof(MyClass).GetFields();
foreach (FieldInfo field in fields) {
    Console.WriteLine(field.Name);
}
获取指定名称的公共字段

Type 类提供了 GetField(string name) 方法,用于根据字段名称获取单个公共字段的 FieldInfo 对象。若字段不存在,返回 null

cs 复制代码
FieldInfo field = typeof(MyClass).GetField("FieldName");

例如:

cs 复制代码
public class MyClass {
    public int PublicField = 100;
    private string _privateField = "Secret";
}

// 获取公共字段
FieldInfo publicField = typeof(MyClass).GetField("PublicField");
Console.WriteLine(publicField?.Name);  // 输出:PublicField

// 获取不存在的字段(返回 null)
FieldInfo invalidField = typeof(MyClass).GetField("NonExistentField");
Console.WriteLine(invalidField == null);  // 输出:True
获取和设置指定字段的值:

定义一个学生类:

cs 复制代码
public class Student {
    public string Name;
    public int Age;
}

对其内容进行修改:

cs 复制代码
using System;
using System.Reflection;

// 创建实例
Student student = new Student();

// 获取 Type 对象
Type type = typeof(Student);

// 获取字段并赋值
FieldInfo nameField = type.GetField("Name");
nameField.SetValue(student, "Alice");  // 设置 Name 字段

FieldInfo ageField = type.GetField("Age");
ageField.SetValue(student, 20);        // 设置 Age 字段

// 读取字段值
Console.WriteLine($"Name: {nameField.GetValue(student)}, Age: {ageField.GetValue(student)}");

请注意,我们这里只是为了方便讲解的在同一个数据集中的类进行代码示例的,实际上我们可能不在同一个数据集的。

4)获取某一个类公共方法
GetMethods()

获取所有公共方法: MethodInfo 是方法的反射信息

cs 复制代码
MethodInfo[] methods = typeof(MyClass).GetMethods();
foreach (MethodInfo method in methods) {
    Console.WriteLine($"方法名: {method.Name}, 返回值类型: {method.ReturnType}");
}

实战!

定义一个测试的类:

cs 复制代码
public class Calculator {
    // 实例方法
    public int Add(int a, int b) {
        return a + b;
    }

    // 重载方法(参数类型不同)
    public double Add(double a, double b) {
        return a + b;
    }

    // 静态方法
    public static void PrintVersion() {
        Console.WriteLine("Calculator v1.0");
    }
}

调用公共方法

①基本调用:实例方法
cs 复制代码
// 创建实例
Calculator calc = new Calculator();

// 获取 Type 对象
Type type = typeof(Calculator);

// 获取方法(无重载时直接按名称获取)
MethodInfo addMethod = type.GetMethod("Add", new Type[] { typeof(int), typeof(int) });

// 调用方法(需传递实例和参数)
object result = addMethod.Invoke(calc, new object[] { 3, 5 });
Console.WriteLine(result); // 输出:8
②处理重载方法:就是什么类型传什么参数
cs 复制代码
// 获取特定重载方法(通过参数类型区分)
MethodInfo doubleAddMethod = type.GetMethod("Add", new Type[] { typeof(double), typeof(double) });

// 调用重载方法
object doubleResult = doubleAddMethod.Invoke(calc, new object[] { 2.5, 3.7 });
Console.WriteLine(doubleResult); // 输出:6.2
调用静态方法
cs 复制代码
// 获取静态方法(无需实例)
MethodInfo staticMethod = type.GetMethod("PrintVersion", BindingFlags.Public | BindingFlags.Static);

// 调用静态方法(第一个参数传 null)
staticMethod.Invoke(null, null); // 输出:Calculator v1.0

关于BindingFlags的解释:

默认情况下,反射方法(如 GetMethod不会自动包含所有成员。例如:

GetMethod("MethodName") 默认仅搜索公共实例方法。

若要搜索静态方法或私有方法,必须显式指定 BindingFlags

BindingFlags这是个什么东西呢?

BindingFlags 是 .NET 中的一个枚举类型,用于指定反射操作时的搜索条件。通过组合不同的标志,可以精确控制反射的行为,例如:

是否包含公共(Public)或非公共(NonPublic)成员。

是否搜索实例(Instance)或静态(Static)成员。

常用标志:

标志 说明
Public 包含公共成员(如 public 方法、属性)。
NonPublic 包含非公共成员(如 privateprotected 方法)。
Instance 包含实例成员(非静态成员)。
Static 包含静态成员(如 static 方法)。
DeclaredOnly 仅搜索当前类中定义的成员(不包含继承的成员)。

所以上面出现的:

BindingFlags.Public | BindingFlags.Static

解释如下:

|按位或运算符 ,用于将多个枚举标志合并为一个复合值。每个 BindingFlags 值对应一个二进制位,组合后可以同时满足多个条件。

完整示例:

cs 复制代码
using System;
using System.Reflection;

public class Program {
    public static void Main() {
        Calculator calc = new Calculator();
        Type type = typeof(Calculator);

        // 1. 调用实例方法
        MethodInfo intAdd = type.GetMethod("Add", new[] { typeof(int), typeof(int) });
        int sum = (int)intAdd.Invoke(calc, new object[] { 3, 5 });
        Console.WriteLine($"3 + 5 = {sum}"); // 输出:3 + 5 = 8

        // 2. 调用重载方法
        MethodInfo doubleAdd = type.GetMethod("Add", new[] { typeof(double), typeof(double) });
        double doubleSum = (double)doubleAdd.Invoke(calc, new object[] { 2.5, 3.7 });
        Console.WriteLine($"2.5 + 3.7 = {doubleSum}"); // 输出:2.5 + 3.7 = 6.2

        // 3. 调用静态方法
        MethodInfo staticMethod = type.GetMethod("PrintVersion", BindingFlags.Public | BindingFlags.Static);
        staticMethod.Invoke(null, null); // 输出:Calculator v1.0
    }
}


其实下面的内容就不是特别的重要了,来慢慢看

5)枚举操作
  • GetEnumNames()

    获取枚举的所有名称:

cs 复制代码
string[] names = typeof(MyEnum).GetEnumNames();  // MyEnum 是枚举类型
foreach (string name in names) {
    Console.WriteLine(name);
}
  • GetEnumValues()

    获取枚举的所有值:

cs 复制代码
Array values = typeof(MyEnum).GetEnumValues();
foreach (object value in values) {
    Console.WriteLine(value);
}

实际使用:

cs 复制代码
using System;

public enum WeekDays { Monday, Tuesday, Wednesday }

public class Program {
    public static void Main() {
        Type enumType = typeof(WeekDays);

        // 获取所有枚举名称
        string[] names = enumType.GetEnumNames();
        Console.WriteLine("枚举名称:");
        foreach (string name in names) {
            Console.WriteLine(name);  // 输出:Monday, Tuesday, Wednesday
        }

        // 获取所有枚举值
        Array values = enumType.GetEnumValues();
        Console.WriteLine("枚举值:");
        foreach (var value in values) {
            Console.WriteLine(value);  // 输出:0, 1, 2
        }
    }
}

其实我感觉这个作用是在本地获取到其他数据集里面的枚举名字,然后调用他那边提供的方法,在我们这个项目集使用别的枚举,感觉没什么意义。除非你这个枚举两边都要有交互,那就代码复杂度提高了。

6)事件操作
  • GetEvents()

    获取所有公共事件:

cs 复制代码
EventInfo[] events = typeof(MyClass).GetEvents();
foreach (EventInfo eventInfo in events) {
    Console.WriteLine(eventInfo.Name);
}
  • GetEvent()

    获取指定名称的事件:

cs 复制代码
EventInfo clickEvent = typeof(Button).GetEvent("Click");

示例:

cs 复制代码
using System;
using System.Reflection;

public class Button {
    public event EventHandler Click;

    public void TriggerClick() {
        Click?.Invoke(this, EventArgs.Empty);
    }
}

public class Program {
    public static void Main() {
        Type type = typeof(Button);

        // 获取所有公共事件
        EventInfo[] events = type.GetEvents();
        Console.WriteLine("所有事件:");
        foreach (EventInfo e in events) {
            Console.WriteLine(e.Name);  // 输出:Click
        }

        // 获取指定名称的事件
        EventInfo clickEvent = type.GetEvent("Click");
        Console.WriteLine($"找到事件: {clickEvent?.Name}");  // 输出:Click
    }
}
7)属性操作
  • GetProperties()

    获取所有公共属性:

cs 复制代码
PropertyInfo[] properties = typeof(MyClass).GetProperties();
foreach (PropertyInfo prop in properties) {
    Console.WriteLine($"属性名: {prop.Name}, 类型: {prop.PropertyType}");
}
  • GetProperty()

    获取指定名称的属性:

cs 复制代码
PropertyInfo nameProp = typeof(Person).GetProperty("Name");

示例:

cs 复制代码
using System;
using System.Reflection;

public class Person {
    public string Name { get; set; }
    public int Age { get; set; }
}

public class Program {
    public static void Main() {
        Type type = typeof(Person);

        // 获取所有公共属性
        PropertyInfo[] properties = type.GetProperties();
        Console.WriteLine("所有属性:");
        foreach (PropertyInfo prop in properties) {
            Console.WriteLine(prop.Name);  // 输出:Name, Age
        }

        // 获取指定名称的属性
        PropertyInfo nameProp = type.GetProperty("Name");
        Console.WriteLine($"找到属性: {nameProp?.Name}");  // 输出:Name
    }
}
8)接口操作
  • GetInterfaces()

    获取类型实现的所有接口:

cs 复制代码
Type[] interfaces = typeof(MyClass).GetInterfaces();
foreach (Type interfaceType in interfaces) {
    Console.WriteLine(interfaceType.FullName);
}
  • GetInterface()

    获取指定名称的接口:

cs 复制代码
Type iDisposable = typeof(MyClass).GetInterface("IDisposable");

示例:

cs 复制代码
// 定义接口
public interface IDisplayer {
    void Display(string message);
}

// 实现接口的类
public class Screen : IDisplayer {
    public void Display(string message) {
        Console.WriteLine($"屏幕显示:{message}");
    }
}
cs 复制代码
using System;
using System.Reflection;

public class Program {
    public static void Main() {
        // 获取 Screen 类的 Type 对象
        Type screenType = typeof(Screen);

        // 检查 Screen 是否实现了 IDisplayer 接口
        Type displayerInterface = screenType.GetInterface("IDisplayer");
        if (displayerInterface != null) {
            Console.WriteLine("Screen 实现了 IDisplayer 接口");
        }

        // 创建 Screen 实例
        object screenInstance = Activator.CreateInstance(screenType);

        // 调用接口方法
        // 需要从接口获取方法,而不是从实现类
        MethodInfo displayMethod = displayerInterface.GetMethod("Display");
        displayMethod.Invoke(screenInstance, new object[] { "Hello, Reflection!" });
    }
}

2. Assembly

Assembly 类表示程序集,用于动态加载和分析程序集内容。

程序集类

主要用来加载其他程序集 加载后

才能用Type来使用其他程序集中的信息

如果使用的不是自己程序集中的内容 需要先加载程序集

比如dll文件(库文件)

简单的把库文件看成一种代码仓库 他提供给使用者一些可以直接拿来用的变量,函数或类
三种加载程序集的函数

一般用来加载在同一文件下的其他程序集

Assembly assembly = Assembly.Load("程序集名字");

一般用来加载不在同一文件下的其他程序集

Assembly assembly = Assembly.LoadFile("要加载的文件的完全限定路径");

Assembly assembly = Assembly.LoadFrom("包含程序集清单的文件的名称或路径 ");

(1)加载程序集的方式

Assembly.Load("程序集名称"): 加载 当前应用程序域已引用位于应用程序基目录(如 bin 的程序集。适用于加载已知程序集名称的依赖项。

例如:

cs 复制代码
using System.Reflection;

// 加载当前项目引用的 Newtonsoft.Json 程序集
try {
    Assembly assembly = Assembly.Load("Newtonsoft.Json");
    Console.WriteLine($"加载成功: {assembly.FullName}");
} catch (FileNotFoundException ex) {
    Console.WriteLine($"加载失败: {ex.Message}");
}

Assembly.LoadFile("完整路径") :通过 完整文件路径 加载程序集,不处理依赖项,适用于加载独立 DLL。

例如:

cs 复制代码
using System.Reflection;

// 加载 D:\Libs\MyLibrary.dll
string path = @"D:\Libs\MyLibrary.dll";
try {
    Assembly assembly = Assembly.LoadFile(path);
    Console.WriteLine($"加载成功: {assembly.FullName}");
} catch (Exception ex) {
    Console.WriteLine($"加载失败: {ex.Message}");
}

注意:

  1. 必须提供完整物理路径(如 @"C:\Folder\File.dll")。

  2. 多次调用同一路径会重复加载程序集(占用内存)。

  3. 不解析依赖项,需手动加载依赖的 DLL。

Assembly.LoadFrom("文件路径"): 通过 文件路径或 UNC 路径 加载程序集,自动处理依赖项,适用于插件系统。

cs 复制代码
using System.Reflection;

// 加载插件程序集(自动处理其依赖项)
string pluginPath = @"D:\Plugins\MyPlugin.dll";
try {
    Assembly assembly = Assembly.LoadFrom(pluginPath);
    Console.WriteLine($"加载成功: {assembly.FullName}");
} catch (Exception ex) {
    Console.WriteLine($"加载失败: {ex.Message}");
}

对比总结

维度 Assembly.LoadFrom Assembly.LoadFile
加载上下文 独立的 LoadFrom 上下文 无关联上下文
依赖项处理 自动解析依赖项 需手动加载依赖项
路径格式 支持相对路径、UNC 路径 必须为完整物理路径
重复加载 同一路径的程序集只加载一次 同一路径的程序集会重复加载
适用场景 插件系统(需处理依赖项) 独立工具模块(无依赖或手动管理依赖)

简单来说,就是**LoadFrom这个路径可以不传完全,可以只传相对路径,就是只传部分路径就可以**


下面都是一些不太重要的内容,了解有这么个东西就行。

(2)获取程序集中的类型
  • GetTypes()
    获取程序集中定义的所有类型:
cs 复制代码
Type[] types = assembly.GetTypes();
foreach (Type type in types) {
    Console.WriteLine(type.FullName);
}
  • GetExportedTypes()
    仅获取公共可见的类型:
cs 复制代码
Type[] publicTypes = assembly.GetExportedTypes();
(3)访问清单信息
  • GetName()

    获取程序集名称和版本:

cs 复制代码
AssemblyName name = assembly.GetName();
Console.WriteLine($"名称: {name.Name}, 版本: {name.Version}");
  • GetReferencedAssemblies()

    获取程序集引用的其他程序集:

cs 复制代码
AssemblyName[] references = assembly.GetReferencedAssemblies();
foreach (AssemblyName refName in references) {
    Console.WriteLine(refName.FullName);
}

3. Activator

Activator 类用于动态创建对象实例。上面我们在Type中不是有一个获取对象示例吗,那个有点麻烦,现在我们来快速的进行实例化对象。

用于快速实例化对象的类

用于将Type对象快捷实例化为对象

先得到Type对象

然后 快速实例化一个对象

(1)创建无参实例

通过反射调用类型的 无参构造函数 创建实例,适用于简单对象的动态初始化。

语法:

object instance = Activator.CreateInstance(Type type);

cs 复制代码
public class MyClass {
    public MyClass() {
        Console.WriteLine("无参构造函数被调用!");
    }
}

// 动态创建实例
Type type = typeof(MyClass);
object instance = Activator.CreateInstance(type);  // 输出:无参构造函数被调用!
注意事项
  • 若类型没有无参构造函数,会抛出 MissingMethodException

  • 可先检查是否存在无参构造函数:

bool hasParameterlessCtor = type.GetConstructor(Type.EmptyTypes) != null;

是不是好方便,少写好几句代码!

和刚才的对比一下。 是不是非常的简结!

(2)创建带参实例

类型具有带参数的公共构造函数。

语法:

object instance = Activator.CreateInstance(

Type type,

params object[] args // 参数必须与构造函数签名严格匹配

);

示例:
cs 复制代码
public class Person {
    public string Name { get; }
    public int Age { get; }

    public Person(string name, int age) {
        Name = name;
        Age = age;
        Console.WriteLine($"带参构造函数被调用!Name={name}, Age={age}");
    }
}

// 动态创建带参实例
Type type = typeof(Person);
object[] args = new object[] { "Alice", 30 };
object instance = Activator.CreateInstance(type, args);  // 输出:带参构造函数被调用!Name=Alice, Age=30
注意事项
  • 若参数类型或数量不匹配,会抛出 MissingMethodExceptionArgumentException

  • 可通过反射精确获取构造函数:

ConstructorInfo ctor = type.GetConstructor(new[] { typeof(string), typeof(int) });

object instance = ctor.Invoke(args);

(3)处理私有构造函数

类型具有私有构造函数(如单例模式、工厂类)。比较鸡肋的操作,它都私有了,还访问它干嘛。

语法:

// 通过反射获取私有构造函数

ConstructorInfo ctor = type.GetConstructor(

BindingFlags.NonPublic | BindingFlags.Instance,

null,

Type.EmptyTypes, // 参数类型数组(无参则为空)

null

);

// 调用私有构造函数

object instance = ctor.Invoke(null);

示例:

cs 复制代码
public class Singleton {
    private static Singleton _instance;

    // 私有构造函数
    private Singleton() {
        Console.WriteLine("私有构造函数被调用!");
    }

    public static Singleton Instance => _instance ??= new Singleton();
}

// 强制通过反射调用私有构造函数
Type type = typeof(Singleton);
ConstructorInfo privateCtor = type.GetConstructor(
    BindingFlags.NonPublic | BindingFlags.Instance, 
    null, 
    Type.EmptyTypes, 
    null
);

object instance = privateCtor.Invoke(null);  // 输出:私有构造函数被调用!



实际操练:

注意你必须将第一个文件的执行文件,可能是exe可能是dll,不管是什么都要放在ReflectionDemoApp文件夹中的bin下才能正常加载数据集

测试用的数据集:

cs 复制代码
using System;

namespace MyReflectionLib {
    internal class Program {
        public static void Main(string[] args) {

        }
    }
    public interface IDisplayable {
        void Display(string msg);
        int GetCode();
    }

    public class DemoClass : IDisplayable {
        public string PublicField = "初始值";
        private string _privateField = "私有内容";

        public string Name { get; set; }

        // 构造函数重载
        public DemoClass() => Console.WriteLine("无参构造执行");
        public DemoClass(string name) => Name = name;

        // 方法重载
        public void ShowMessage(string msg) => Console.WriteLine($"字符串消息: {msg}");
        public void ShowMessage(int code) => Console.WriteLine($"数字代码: {code}");
        public string CombineMessages(string a, string b) => a + " + " + b;

        // 接口实现
        public void Display(string msg) => Console.WriteLine($"显示消息: {msg}");
        public int GetCode() => new Random().Next(1000);

        // 特殊方法
        public override string ToString() => $"DemoClass实例[{Name}]";
    }
}

访问用的代码:

cs 复制代码
using System;
using System.Linq;
using System.Reflection;

namespace ReflectionDemoApp {
    class Program {
        static void Main(string[] args) {
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine("=============== 程序集加载 ===============");
            Console.ResetColor();

            // 动态加载程序集
            Assembly assembly = LoadAssembly("MyReflectionLib.dll");
            if (assembly == null) return;

            // 获取目标类型
            Type demoType = assembly.GetType("MyReflectionLib.DemoClass");
            Console.WriteLine($"\n加载类型: {demoType.FullName}");

            /******************** 构造函数操作 ********************/
            PrintSectionHeader("构造函数演示", ConsoleColor.Yellow);

            // 1. 无参构造函数
            var obj1 = CreateInstance(demoType, "无参实例");
            // 2. 带参构造函数
            var obj2 = CreateInstance(demoType, "带参实例", new object[] { "Alice" });

            /******************** 字段和属性操作 ********************/
            PrintSectionHeader("字段和属性操作", ConsoleColor.Green);

            // 修改公共字段
            ModifyPublicField(obj1, "PublicField", "新公共字段值");
            // 修改属性
            ModifyProperty(obj2, "Name", "Bob");
            // 访问私有字段
            AccessPrivateField(obj1, "_privateField");

            /******************** 方法调用操作 ********************/
            PrintSectionHeader("方法调用演示", ConsoleColor.Magenta);

            // 调用所有公共方法
            InvokeAllPublicMethods(obj1, new object[] { "测试消息", 123 });

            /******************** 枚举操作 ********************/
            PrintSectionHeader("枚举操作演示", ConsoleColor.Blue);
            ShowEnumValues(assembly, "MyReflectionLib.Status");

            /******************** 接口操作 ********************/
            PrintSectionHeader("接口操作演示", ConsoleColor.Cyan);
            InvokeInterfaceMethod(obj1, "MyReflectionLib.IDisplayable", "Display", "接口调用测试");

            Console.ReadKey();
        }

        #region Helper Methods

        // ---------------------- 程序集加载 ----------------------
        static Assembly LoadAssembly(string path) {
            try {
                var assembly = Assembly.LoadFrom(path);
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine($"成功加载程序集: {System.IO.Path.GetFileName(path)}");
                Console.ResetColor();
                return assembly;
            }
            catch (Exception ex) {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine($"加载失败: {ex.Message}");
                Console.ResetColor();
                return null;
            }
        }

        // ---------------------- 实例化对象 ----------------------
        static object CreateInstance(Type type, string description, object[] args = null) {
            try {
                Console.ForegroundColor = ConsoleColor.White;
                var instance = args == null ?
                    Activator.CreateInstance(type) :
                    Activator.CreateInstance(type, args);

                Console.WriteLine($"✔ {description}创建成功");
                if (args != null) Console.WriteLine($"   参数: [{string.Join(", ", args)}]");
                return instance;
            }
            catch (Exception ex) {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine($"创建失败: {ex.Message}");
                return null;
            }
            finally {
                Console.ResetColor();
            }
        }

        // ---------------------- 修改公共字段 ----------------------
        static void ModifyPublicField(object obj, string fieldName, object value) {
            try {
                var field = obj.GetType().GetField(fieldName);
                field.SetValue(obj, value);
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine($"字段 [{fieldName}] 修改成功 → 新值: {field.GetValue(obj)}");
            }
            catch (Exception ex) {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine($"修改字段失败: {ex.Message}");
            }
            finally {
                Console.ResetColor();
            }
        }

        // ---------------------- 修改属性 ----------------------
        static void ModifyProperty(object obj, string propertyName, object value) {
            try {
                var prop = obj.GetType().GetProperty(propertyName);
                if (prop != null && prop.CanWrite) {
                    prop.SetValue(obj, value);
                    Console.ForegroundColor = ConsoleColor.Green;
                    Console.WriteLine($"属性 [{propertyName}] 修改成功 → 新值: {prop.GetValue(obj)}");
                } else {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine($"属性 [{propertyName}] 不存在或不可写!");
                }
            }
            catch (Exception ex) {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine($"修改属性失败: {ex.Message}");
            }
            finally {
                Console.ResetColor();
            }
        }

        // ---------------------- 访问私有字段 ----------------------
        static void AccessPrivateField(object obj, string fieldName) {
            try {
                var field = obj.GetType().GetField(fieldName,
                    BindingFlags.NonPublic | BindingFlags.Instance);
                if (field != null) {
                    Console.ForegroundColor = ConsoleColor.DarkYellow;
                    Console.WriteLine($"私有字段 [{fieldName}] 值: {field.GetValue(obj)}");
                } else {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine($"私有字段 [{fieldName}] 不存在!");
                }
            }
            catch (Exception ex) {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine($"访问私有字段失败: {ex.Message}");
            }
            finally {
                Console.ResetColor();
            }
        }

        // ---------------------- 调用所有公共方法 ----------------------
        static void InvokeAllPublicMethods(object obj, params object[] parameters) {
            var methods = obj.GetType().GetMethods();
            Console.WriteLine($"\n共发现 {methods.Length} 个公共方法:");

            foreach (var method in methods) {
                if (method.IsSpecialName) continue; // 跳过属性/事件方法

                Console.ForegroundColor = ConsoleColor.DarkCyan;
                Console.Write($"\n▶ 尝试调用: {method.Name}");
                Console.ResetColor();
                Console.Write($" ({string.Join(", ", method.GetParameters().Select(p => p.ParameterType.Name))})");

                try {
                    object result = method.Invoke(obj, MatchParameters(method, parameters));
                    Console.ForegroundColor = ConsoleColor.Green;
                    Console.WriteLine($" ✔ 调用成功");
                    if (result != null) Console.WriteLine($"   返回值: {result}");
                }
                catch (Exception ex) {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine($" ✘ 调用失败: {ex.InnerException?.Message ?? ex.Message}");
                }
                finally {
                    Console.ResetColor();
                }
            }
        }

        // ---------------------- 智能参数匹配 ----------------------
        static object[] MatchParameters(MethodInfo method, object[] parameters) {
            return parameters.Take(method.GetParameters().Length).ToArray();
        }

        // ---------------------- 枚举操作 ----------------------
        static void ShowEnumValues(Assembly assembly, string enumTypeName) {
            try {
                Type enumType = assembly.GetType(enumTypeName);
                if (enumType == null || !enumType.IsEnum) {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine($"枚举类型 [{enumTypeName}] 不存在或不是枚举类型!");
                    return;
                }

                Console.ForegroundColor = ConsoleColor.Blue;
                Console.WriteLine($"\n枚举 [{enumType.Name}] 所有值:");

                Array values = Enum.GetValues(enumType);
                foreach (var value in values) {
                    Console.WriteLine($"  {value} = {(int)value}");
                }
            }
            catch (Exception ex) {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine($"枚举操作失败: {ex.Message}");
            }
            finally {
                Console.ResetColor();
            }
        }

        // ---------------------- 接口方法调用 ----------------------
        static void InvokeInterfaceMethod(object obj, string interfaceName, string methodName, params object[] args) {
            try {
                Type interfaceType = obj.GetType().GetInterface(interfaceName);
                if (interfaceType == null) {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine($"对象未实现接口 [{interfaceName}]!");
                    return;
                }

                MethodInfo method = interfaceType.GetMethod(methodName);
                if (method == null) {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine($"接口 [{interfaceName}] 中不存在方法 [{methodName}]!");
                    return;
                }

                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine($"\n通过接口 [{interfaceType.Name}] 调用方法 [{methodName}]");

                object result = method.Invoke(obj, args);
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("✔ 接口方法调用成功");
                if (result != null) Console.WriteLine($"   返回值: {result}");
            }
            catch (Exception ex) {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine($"接口方法调用失败: {ex.InnerException?.Message ?? ex.Message}");
            }
            finally {
                Console.ResetColor();
            }
        }

        // ---------------------- 模块标题打印 ----------------------
        static void PrintSectionHeader(string text, ConsoleColor color) {
            Console.WriteLine();
            Console.ForegroundColor = color;
            Console.WriteLine($"══════════ {text} ══════════");
            Console.ResetColor();
        }

        #endregion
    }
}

结果:

四、总结

反射核心类总结表

类名 API / 属性 描述 示例
Type typeof(MyClass) 获取类型的 Type 对象(编译时已知类型)。 Type type = typeof(MyClass);
obj.GetType() 通过实例获取 Type 对象。 Type type = new MyClass().GetType();
Type.GetType("Namespace.MyClass") 通过类型全名获取 Type 对象(需包含程序集信息)。 Type type = Type.GetType("MyReflectionLib.DemoClass, MyReflectionLib");
type.Assembly 获取类型所属的程序集。 Assembly assembly = type.Assembly;
type.GetMethods() 获取所有公共方法。 MethodInfo[] methods = type.GetMethods();
type.GetMethod("MethodName", Type[] paramTypes) 获取指定名称和参数类型的公共方法。 MethodInfo method = type.GetMethod("Add", new[] { typeof(int) });
type.GetProperties() 获取所有公共属性。 PropertyInfo[] props = type.GetProperties();
type.GetProperty("PropertyName") 获取指定名称的公共属性。 PropertyInfo prop = type.GetProperty("Name");
type.GetFields() 获取所有公共字段。 FieldInfo[] fields = type.GetFields();
type.GetField("FieldName", BindingFlags) 获取指定名称的字段(可指定 NonPublic 访问私有字段)。 FieldInfo field = type.GetField("_private", BindingFlags.NonPublic);
type.GetConstructors() 获取所有公共构造函数。 ConstructorInfo[] ctors = type.GetConstructors();
type.GetConstructor(Type[] paramTypes) 获取匹配参数类型的构造函数。 ConstructorInfo ctor = type.GetConstructor(new[] { typeof(string) });
type.GetInterfaces() 获取类型实现的所有接口。 Type[] interfaces = type.GetInterfaces();
type.GetInterface("InterfaceName") 获取指定名称的接口。 Type iface = type.GetInterface("IDisplayable");
type.GetEnumNames() 获取枚举类型的所有名称(仅适用于枚举)。 string[] names = typeof(Status).GetEnumNames();
type.GetEvents() 获取所有公共事件。 EventInfo[] events = type.GetEvents();
Assembly Assembly.Load("AssemblyName") 加载当前应用程序域已知的程序集(基于名称)。 Assembly assembly = Assembly.Load("MyReflectionLib");
Assembly.LoadFrom("FilePath") 通过文件路径加载程序集(自动处理依赖项)。 Assembly assembly = Assembly.LoadFrom("Plugins/MyPlugin.dll");
Assembly.LoadFile("FullPath") 通过完整路径加载程序集(不处理依赖项)。 Assembly assembly = Assembly.LoadFile(@"C:\Lib\MyLib.dll");
assembly.GetTypes() 获取程序集中定义的所有类型。 Type[] types = assembly.GetTypes();
assembly.GetExportedTypes() 获取程序集中所有公共可见的类型。 Type[] publicTypes = assembly.GetExportedTypes();
assembly.GetName() 获取程序集名称和版本信息。 AssemblyName name = assembly.GetName();
assembly.GetReferencedAssemblies() 获取程序集引用的其他程序集。 AssemblyName[] refs = assembly.GetReferencedAssemblies();
Activator Activator.CreateInstance(Type) 创建无参构造函数的实例。 object obj = Activator.CreateInstance(typeof(MyClass));
Activator.CreateInstance(Type, object[] args) 创建带参构造函数的实例。 object obj = Activator.CreateInstance(type, new[] { "Alice" });
Activator.CreateInstance(Type, BindingFlags, ...) 创建实例(可指定 NonPublic 访问私有构造函数)。 object obj = Activator.CreateInstance(type, nonPublic: true);
相关推荐
元亓亓亓19 分钟前
Java后端开发day36--源码解析:HashMap
java·开发语言·数据结构
道剑剑非道23 分钟前
QT 打包安装程序【windeployqt.exe】报错c000007d原因:Conda巨坑
开发语言·qt·conda
豆沙沙包?1 小时前
5.学习笔记-SpringMVC(P61-P70)
数据库·笔记·学习
小邓儿◑.◑1 小时前
C++武功秘籍 | 入门知识点
开发语言·c++
每次的天空2 小时前
Android学习总结之Room篇
android·学习·oracle
码银3 小时前
Java 集合:泛型、Set 集合及其实现类详解
java·开发语言
大G哥3 小时前
PHP标签+注释+html混写+变量
android·开发语言·前端·html·php
傻啦嘿哟3 小时前
HTTP代理基础:网络新手的入门指南
开发语言·php
fish_study_csdn3 小时前
pytest 技术总结
开发语言·python·pytest
Nuyoah.4 小时前
《Vue3学习手记5》
前端·javascript·学习