深入理解 C# 反射:基础原理与实际应用

news/2025/3/13 0:04:41/

反射(Reflection)是 .NET 中一项强大的功能,它允许程序在运行时动态获取类型信息、创建对象、调用方法、以及访问和修改类的成员(字段、属性等)。反射提供了极大的灵活性和可扩展性,特别适用于那些需要在运行时动态操作类型的场景。然而,反射也有性能开销,因此在使用时需要谨慎。本文将详细介绍 C# 反射的基本用法,包括如何获取类型信息、动态创建对象、动态调用方法、访问和修改字段/属性的值,以及获取程序集和模块信息。

1. 获取类型信息(字段、属性、方法等)

反射允许你在运行时获取对象的类型信息,进而访问类的字段、属性、方法等成员。你可以通过 Type 类来获取类型信息,利用该信息进行动态操作。

示例:获取字段、属性和方法信息
using System;
using System.Reflection;public class MyClass
{public int Field1 = 10;private string Field2 = "Hello";public int MyProperty { get; set; }public void MyMethod(){Console.WriteLine("Method executed!");}private void PrivateMethod(){Console.WriteLine("Private method executed!");}
}class Program
{static void Main(){MyClass obj = new MyClass();Type type = obj.GetType(); // 获取类型信息// 获取字段信息FieldInfo field1 = type.GetField("Field1");Console.WriteLine("Field1: " + field1.GetValue(obj));  // 输出:Field1: 10// 获取私有字段信息FieldInfo field2 = type.GetField("Field2", BindingFlags.NonPublic | BindingFlags.Instance);Console.WriteLine("Field2: " + field2.GetValue(obj));  // 输出:Field2: Hello// 获取属性信息PropertyInfo property = type.GetProperty("MyProperty");Console.WriteLine("MyProperty: " + property.GetValue(obj));  // 输出:MyProperty: 0 (默认值)// 获取方法信息MethodInfo method = type.GetMethod("MyMethod");method.Invoke(obj, null);  // 输出:Method executed!// 获取私有方法信息MethodInfo privateMethod = type.GetMethod("PrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);privateMethod.Invoke(obj, null);  // 输出:Private method executed!}
}

在上面的代码中,Type.GetType() 用来获取类的类型信息,GetField(), GetProperty(), GetMethod() 等方法可以帮助你获取字段、属性和方法的信息。通过 BindingFlags.NonPublic 可以访问私有成员。

2. 动态创建对象

反射使得在运行时动态创建对象成为可能。你可以通过类型信息来创建对象,甚至是使用带参数的构造函数来创建对象。

示例:动态创建对象并调用方法
using System;public class MyClass
{public MyClass() { Console.WriteLine("Constructor called!"); }public void DisplayMessage() { Console.WriteLine("Hello from MyClass!"); }
}class Program
{static void Main(){Type type = typeof(MyClass);// 动态创建对象object obj = Activator.CreateInstance(type);// 调用方法MethodInfo method = type.GetMethod("DisplayMessage");method.Invoke(obj, null);  // 输出:Hello from MyClass!}
}

在这个示例中,Activator.CreateInstance() 方法用来动态创建 MyClass 类的实例,之后通过反射调用了 DisplayMessage 方法。

3. 动态调用方法

通过反射,你可以在运行时动态调用对象的公共或私有方法。即使方法的名称、参数等信息在编译时未知,也可以通过反射来动态调用。

示例:动态调用公共和私有方法
using System;
using System.Reflection;public class MyClass
{private void PrivateMethod(){Console.WriteLine("Private method called!");}public void PublicMethod(){Console.WriteLine("Public method called!");}
}class Program
{static void Main(){MyClass obj = new MyClass();Type type = obj.GetType();// 调用公共方法MethodInfo publicMethod = type.GetMethod("PublicMethod");publicMethod.Invoke(obj, null);  // 输出:Public method called!// 调用私有方法MethodInfo privateMethod = type.GetMethod("PrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);privateMethod.Invoke(obj, null);  // 输出:Private method called!}
}

在上面的代码中,我们使用 MethodInfo.Invoke() 方法动态调用了公共方法 PublicMethod 和私有方法 PrivateMethod

4. 访问和修改字段、属性的值

反射不仅能够读取对象的字段和属性,还能修改它们的值。你可以通过反射动态地访问和修改字段或属性,无论它们是公共的还是私有的。

示例:访问和修改字段、属性的值
using System;
using System.Reflection;public class MyClass
{public int PublicField = 100;private string PrivateField = "Hello";public int MyProperty { get; set; }
}class Program
{static void Main(){MyClass obj = new MyClass();Type type = obj.GetType();// 获取和修改字段值FieldInfo publicField = type.GetField("PublicField");Console.WriteLine("PublicField: " + publicField.GetValue(obj));  // 输出:PublicField: 100publicField.SetValue(obj, 200);Console.WriteLine("PublicField (after modification): " + publicField.GetValue(obj));  // 输出:PublicField (after modification): 200// 获取私有字段值FieldInfo privateField = type.GetField("PrivateField", BindingFlags.NonPublic | BindingFlags.Instance);Console.WriteLine("PrivateField: " + privateField.GetValue(obj));  // 输出:PrivateField: HelloprivateField.SetValue(obj, "New Value");Console.WriteLine("PrivateField (after modification): " + privateField.GetValue(obj));  // 输出:PrivateField (after modification): New Value// 获取和修改属性值PropertyInfo property = type.GetProperty("MyProperty");property.SetValue(obj, 50);Console.WriteLine("MyProperty: " + property.GetValue(obj));  // 输出:MyProperty: 50}
}

在这个示例中,FieldInfo.GetValue()PropertyInfo.GetValue() 方法用于读取字段或属性的值,FieldInfo.SetValue()PropertyInfo.SetValue() 方法则用于修改字段或属性的值。

5. 获取程序集和模块信息

反射还可以用于获取当前程序集中所有的类型、方法、属性等信息。程序集(Assembly)是包含类型定义的容器,而模块(Module)是程序集中的组成部分。通过反射,可以获取程序集和模块的信息。

示例:获取程序集和模块信息
using System;
using System.Reflection;class Program
{static void Main(){// 获取当前程序集Assembly assembly = Assembly.GetExecutingAssembly();Console.WriteLine("Assembly: " + assembly.FullName);// 获取程序集中的所有类型Console.WriteLine("\nTypes in Assembly:");foreach (Type type in assembly.GetTypes()){Console.WriteLine(type.FullName);}// 获取模块信息Console.WriteLine("\nModules in Assembly:");foreach (Module module in assembly.GetModules()){Console.WriteLine("Module: " + module.Name);}}
}

在这个示例中,使用 Assembly.GetExecutingAssembly() 获取当前程序的程序集信息,通过 GetTypes() 方法列出程序集中的所有类型,GetModules() 获取程序集中的模块信息。

6. 总结

反射是 C# 中非常强大且灵活的功能,它允许程序在运行时动态获取类型信息、创建对象、调用方法、访问和修改字段及属性的值。常见的反射用途包括:

  1. 获取类型信息:通过 Type 获取类的字段、属性、方法等信息。
  2. 动态创建对象:使用 Activator.CreateInstance() 动态创建对象。
  3. 动态调用方法:通过 MethodInfo.Invoke() 动态调用对象的方法。
  4. 访问和修改字段/属性:使用反射读取和修改对象的字段或属性值。
  5. 获取程序集和模块信息:通过反射获取程序集和模块的详细信息。

尽管反射提供了极大的灵活性,但由于其性能开销较大,因此应谨慎使用,尤其是在性能要求较高的场景中。反射通常用于需要高度灵活性和可扩展性的场景,例如插件框架、动态代理和序列化等。


http://www.ppmy.cn/news/1578650.html

相关文章

C#线程上异步执行(this.BeginInvoke)

在C#中,this.BeginInvoke 是一个用于在UI线程上异步执行代码的方法。它通常用于在Windows Forms应用程序中,当需要在UI线程上更新UI控件,但当前代码运行在非UI线程上时。 this.BeginInvoke((MethodInvoker)delegate {// 在这里更新UI控件 })…

GitHub 项目版本管理与 Release 发布教程

GitHub 项目版本管理与 Release 发布教程 本教程适用于希望在 GitHub 上管理代码版本并发布 Release 的开发者。适用于 Git 基础用户,涵盖从代码提交到发布 Release 的完整流程。 1. 配置 Git 环境 1.1 安装 Git 如果尚未安装 Git,可以在 Git 官方网…

【Spring IOC/AOP】

IOC 参考: Spring基础 - Spring核心之控制反转(IOC) | Java 全栈知识体系 (pdai.tech) 概述: Ioc 即 Inverse of Control (控制反转),是一种设计思想,就是将原本在程序中手动创建对象的控制权&#xff…

深度学习笔记——残差网络和模型选择

在B站上听李沐老师的课记录的笔记 1.残差神经网络(Residual Neural Network,简称ResNet)属于深度学习模型的一种,其核心在于让网络的每一层不直接学习预期输出,而是学习与输入之间的残差关系。 残差块使得很深的网络更…

【python|二分|leetcode441】一题搞清楚二分区间问题---闭区间、左闭右开、左开右闭、全开区间

every blog every motto: Although the world is full of suffering, it is full also of the overcoming of it 0. 前言 一题搞清楚二分区间问题—闭区间、左闭右开、左开右闭、全开区间 0.1 题目:Problem: 441. 排列硬币 你总共有 n 枚硬币&#x…

Kubernetes服务部署 —— Kafka

1、简介 Kafka和zookeeper是两种典型的有状态的应用集群服务。首先kafka和zookeeper都需要存储盘来保存有状态信息;其次kafka和zookeeper每一个实例都需要有对应的实例Id (Kafka需broker.id, zookeeper需要my.id) 来作为集群内部每个成员的标识,集群内节…

Linux内核实时机制18 - RT调度器1 - 数据结构

文章目录 1、Linux调度概述2、实时调度类 rt_sched_class2.1、SCHED_FIFO 调度策略2.2、SCHED_RR 调度策略3、实时调度相关数据结构3.1、实时调度实体 sched_rt_entity3.2、优先级队列rt_prio_array3.3、实时就绪队列 rt_rq3.4、带宽控制结构体 rt_bandwidth3.5、组调度结构体 …

slf4j和log4j的区别与使用

slf4j和log4j的区别与使用 文章目录 1.简介2.使用教程3.常见报错解决(持续更新) 1.简介 官网:https://www.slf4j.org/manual.html (1)简单日记门面(simple logging Facade for Java)SLF4J是为各种loging APIs提供一个简单统一的接口。 (…