C# 反射(Reflection)超全解析

一、反射(Reflection)的清晰定义

反射(Reflection) 是 .NET 框架提供的核心运行时机制,它允许程序在运行时而非编译时

  • 获取程序集(Assembly)、模块(Module)、类型(Type)的元数据信息
  • 动态创建对象实例
  • 动态调用方法
  • 动态读取或修改属性/字段
  • 动态解析自定义特性(Attribute)

对比理解

方式 特点
普通编码 编译期确定类型,直接调用,性能高
反射编程 运行期确定类型,动态操作,极度灵活

通俗理解

  • 普通编程:编译期就确定要使用哪个类、调用哪个方法,代码是"写死的"。
  • 反射编程:程序运行时才知道要操作哪个类,通过读取元数据"动态操作类型"。

👉 一句话:反射 = 运行时读取元数据 + 动态操作类型


二、.NET 中反射的底层基础

IL 与元数据

  • C# → 编译 → IL(中间语言)
  • 同时生成 Metadata(元数据表)(包含类型名、命名空间、成员签名、特性信息等)

反射的本质:

读取程序集中的 Metadata,而不是"反编译源码"


三、反射核心类继承体系与关键成员

1. 核心类继承结构

text 复制代码
MemberInfo
 ├── FieldInfo
 ├── PropertyInfo
 ├── MethodBase
 │    ├── ConstructorInfo
 │    └── MethodInfo
 └── EventInfo

MemberInfo 是所有"成员描述类"的抽象基类。

2. 各核心类的关键成员(常用)

类名 核心父类 关键成员 作用说明
MemberInfo - Name、GetCustomAttributes() 获取成员名称、自定义特性
MethodBase MemberInfo IsAbstract、IsPublic、IsStatic、Invoke() 判断方法特性、调用方法或构造函数
ConstructorInfo MethodBase Invoke(object[] parameters) 调用构造函数创建实例
MethodInfo MethodBase ReturnType、Invoke() 获取返回值类型、调用方法
PropertyInfo MemberInfo CanRead、CanWrite、PropertyType、GetValue()、SetValue() 获取/设置属性值

四、Type 类深度解析(核心入口)

Type 是整个反射体系的入口类,几乎所有反射操作都从它开始。

常用成员

1. 类型判断

  • IsInterfaceIsArrayIsPrimitiveIsEnum
  • IsClassIsPublic

2. 类型信息

  • Name(类型名)、FullName(完整类型名)、BaseType(基类)

3. 类型关系判断

  • IsInstanceOfType(object obj)
  • IsAssignableFrom(Type type)

4. 成员获取

  • GetConstructor(Type[] paramTypes)
  • GetMethod(string name, Type[] paramTypes)
  • GetProperty(string name)

基础示例

csharp 复制代码
Type type = typeof(Person);

Console.WriteLine(type.Name);
Console.WriteLine(type.FullName);
Console.WriteLine(type.BaseType);
Console.WriteLine(type.IsClass);
Console.WriteLine(type.IsPublic);

运行结果:

text 复制代码
Person
ReflectionDemo.Person
System.Object
True
True

五、核心反射类型详解(附示例)

1. MemberInfo

csharp 复制代码
Type type = typeof(Person);
foreach (MemberInfo member in type.GetMembers())
{
    Console.WriteLine(member.Name + " - " + member.MemberType);
}

运行结果示例:

text 复制代码
get_Name - Method
set_Name - Method
SayHi - Method
Name - Property

2. MethodInfo(方法反射)

获取并调用无参方法

csharp 复制代码
MethodInfo method = type.GetMethod("SayHi");
method.Invoke(personObj, null);

运行结果:

text 复制代码
Hi, my name is chen

调用带参数方法

csharp 复制代码
MethodInfo method2 = type.GetMethod(
    "SayHi",
    new Type[] { typeof(string) }
);

method2.Invoke(personObj, new object[] { "Tom" });

运行结果:

text 复制代码
Hi, Tom

3. ConstructorInfo(构造函数反射)

csharp 复制代码
ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes);
object obj = ctor.Invoke(null);

等价于:

csharp 复制代码
object obj = Activator.CreateInstance(type);

4. PropertyInfo(属性反射)

csharp 复制代码
PropertyInfo prop = type.GetProperty("Name");

// 设置值
prop.SetValue(obj, "chen");

// 获取值
string name = (string)prop.GetValue(obj);

输出:

text 复制代码
chen

六、完整反射综合示例

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

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

        // 创建实例
        object obj = Activator.CreateInstance(type);

        // 设置属性
        PropertyInfo prop = type.GetProperty("Name");
        prop.SetValue(obj, "chen");

        // 调用无参方法
        MethodInfo m1 = type.GetMethod("SayHi");
        m1.Invoke(obj, null);

        // 调用带参方法
        MethodInfo m2 = type.GetMethod(
            "SayHi",
            new[] { typeof(string) }
        );
        m2.Invoke(obj, new object[] { "Jack" });
    }
}

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

    public void SayHi()
    {
        Console.WriteLine($"Hi, my name is {Name}");
    }

    public void SayHi(string name)
    {
        Console.WriteLine($"Hi, {name}");
    }
}

运行结果:

text 复制代码
Hi, my name is chen
Hi, Jack

七、反射实现通用对象拷贝(完整 + 验证)

1. 拷贝方法(浅拷贝)

csharp 复制代码
static object MyClone(object source)
{
    if (source == null)
        throw new ArgumentNullException(nameof(source));

    Type type = source.GetType();
    object target = Activator.CreateInstance(type);

    foreach (PropertyInfo prop in type.GetProperties())
    {
        if (prop.CanRead && prop.CanWrite)
        {
            prop.SetValue(target, prop.GetValue(source));
        }
    }

    return target;
}

2. 验证代码

csharp 复制代码
Person p1 = new Person { Name = "chen", Age = 12 };
Person p2 = (Person)MyClone(p1);

p2.Name = "zhangsan";

Console.WriteLine(p1.Name);
Console.WriteLine(p2.Name);

运行结果:

text 复制代码
chen
zhangsan

特点说明

  • ✅ 支持任意对象、不依赖具体类型
  • ❌ 仅实现浅拷贝
  • ❌ 要求目标类型有无参构造函数

八、反射的典型使用场景(工程级)

1. 框架与底层组件

  • ORM(EF Core、Dapper)
  • 依赖注入(Autofac)
  • 序列化(Newtonsoft.Json、System.Text.Json)

2. 插件化架构

  • 动态加载 DLL
  • 运行时发现并执行模块

3. 通用基础工具

  • 对象拷贝
  • 表单/模型验证
  • 通用 Mapper

九、反射的代价、注意事项与优化方案

1. 性能问题

  • 反射 ≈ 普通调用的 5~20 倍开销
  • 优化方案:
    • 缓存 Type / MethodInfo / PropertyInfo 等反射对象
    • 结合表达式树(Expression)优化
    • 使用 Delegate.CreateDelegate 转为委托调用

2. 封装性破坏

  • 反射可绕过访问修饰符访问私有成员
  • 建议:框架级场景使用,业务层慎用

3. 类型安全

  • 编译期无法校验反射操作的类型正确性
  • 必须做好异常处理(如 NullReferenceExceptionMissingMethodException 等)

十、总结(工程视角)

  • 反射是 .NET 框架能力的地基,是框架的灵魂
  • 核心入口:Type 类;核心成员描述类:MethodInfo / PropertyInfo / ConstructorInfo
  • 优点:高度灵活、解耦,框架开发必备
  • 缺点:性能损耗、类型不安全、破坏封装性
  • 业务代码应"少而精"使用,框架代码必须掌握

反射不是为了偷懒,而是为了"抽象与解耦"。


相关推荐
回家路上绕了弯1 天前
MDC日志链路追踪实战:让分布式系统问题排查更高效
分布式·后端
源代码•宸1 天前
goframe框架签到系统项目开发(补签逻辑实现、编写Lua脚本实现断签提醒功能、简历示例)
数据库·后端·中间件·go·lua·跨域·refreshtoken
武子康1 天前
大数据-205 线性回归的机器学习视角:矩阵表示、SSE损失与最小二乘
大数据·后端·机器学习
唐叔在学习1 天前
才知道python还可以这样发消息提醒的
后端·python·程序员
程序员爱钓鱼1 天前
Node.js 编程实战:前后端结合 - 前端静态资源优化
前端·后端·node.js
程序员爱钓鱼1 天前
Node.js 编程实战:前后端结合 - 跨域与代理配置
前端·后端·node.js
闲人编程1 天前
电商平台用户系统API设计
数据库·后端·消息队列·fastapi·监控·容器化·codecapsule
烈焰飞鸟1 天前
华为云前后端部署实战手册
运维·前端·vue.js·后端·华为云
Clarence Liu1 天前
快慢指针问题
后端·算法