目 录CONTENT

文章目录

CSharp(五十八) 反射(Reflection)详解

C# 反射(Reflection)详解


一、什么是反射?

1.1 生活比喻

反射就像是一台X 光机

平时你看一个人,只能看到外表(调用公开的方法和属性)。
用 X 光机一照,你能看到他的骨骼、器官——连藏在身体里的东西都看得一清二楚。

反射就是程序的"X 光机"——运行时照一照一个类或对象,就能知道:

  • 它叫什么名字
  • 有哪些属性、方法、字段
  • 这些成员是 public 还是 private
  • 甚至可以调用 private 方法、修改 private 字段

1.2 一句话理解

反射 = 程序在运行时"照镜子"看自己的能力。 通过反射,代码可以动态地获取类型信息、创建对象、调用方法、访问字段,而不需要在编译时就知道具体类型。

1.3 反射能做什么?

// 不用反射——编译时就确定了
Person p = new Person();
p.Name = "张三";
p.SayHello();

// 用反射——运行时才确定
Type type = Type.GetType("Person");           // 1. 运行时获取类型
object obj = Activator.CreateInstance(type);  // 2. 运行时创建对象
PropertyInfo prop = type.GetProperty("Name"); // 3. 运行时获取属性
prop.SetValue(obj, "张三");                    // 4. 运行时设置属性
MethodInfo method = type.GetMethod("SayHello");// 5. 运行时获取方法
method.Invoke(obj, null);                     // 6. 运行时调用方法

二、获取类型信息 —— Type 对象

2.1 三种方式获取 Type

// 方式一:typeof(编译时已知类型)
Type t1 = typeof(string);
Type t2 = typeof(List<int>);
Type t3 = typeof(Program);

// 方式二:对象.GetType()(有实例时)
string s = "hello";
Type t4 = s.GetType();

Person p = new Person();
Type t5 = p.GetType();

// 方式三:Type.GetType()(通过字符串名称,运行时动态)
Type t6 = Type.GetType("System.String");       // 需要完整命名空间
Type t7 = Type.GetType("MyApp.Person");        // 自定义类要写全命名空间

// 方式四:Assembly.GetType()(从程序集中获取)
Type t8 = Assembly.GetExecutingAssembly().GetType("MyApp.Person");

三种方式对比:

方式 场景 示例
typeof(T) 编译时就知道类型 typeof(int)
obj.GetType() 有一个对象实例 "hello".GetType()
Type.GetType("名字") 运行时才知道类型名 Type.GetType("System.String")

2.2 Type 对象能告诉你什么?

using System;
using System.Reflection;

public class Person
{
    public string Name { get; set; }
    private int _age;

    public Person() { }
    public Person(string name) => Name = name;

    public void SayHello() => Console.WriteLine($"你好,我是{Name}");
    private void Secret() => Console.WriteLine("秘密方法");
}

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

        Console.WriteLine($"类名: {type.Name}");
        Console.WriteLine($"全名: {type.FullName}");
        Console.WriteLine($"命名空间: {type.Namespace}");
        Console.WriteLine($"是类吗: {type.IsClass}");
        Console.WriteLine($"是公开的吗: {type.IsPublic}");
        Console.WriteLine($"基类: {type.BaseType?.Name}");
    }
}

输出:

类名: Person
全名: Program+Person
命名空间:
是类吗: True
是公开的吗: True
基类: Object

三、查看类的成员 —— 反射的"扫描"功能

3.1 获取构造函数

Type type = typeof(Person);

ConstructorInfo[] constructors = type.GetConstructors();
foreach (ConstructorInfo ctor in constructors)
{
    ParameterInfo[] parameters = ctor.GetParameters();
    string paramStr = string.Join(", ",
        parameters.Select(p => $"{p.ParameterType.Name} {p.Name}"));
    Console.WriteLine($"构造函数: Person({paramStr})");
}

输出:

构造函数: Person()
构造函数: Person(String name)

3.2 获取属性

Type type = typeof(Person);

PropertyInfo[] properties = type.GetProperties();  // 只获取 public 属性
foreach (PropertyInfo prop in properties)
{
    Console.WriteLine($"属性: {prop.PropertyType.Name} {prop.Name}");
    Console.WriteLine($"  可读: {prop.CanRead}, 可写: {prop.CanWrite}");
}

输出:

属性: String Name
  可读: True, 可写: True
// 获取所有属性(包括私有属性)
PropertyInfo[] allProps = type.GetProperties(
    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

3.3 获取方法

Type type = typeof(Person);

MethodInfo[] methods = type.GetMethods();  // 只获取 public 方法
foreach (MethodInfo method in methods)
{
    ParameterInfo[] parameters = method.GetParameters();
    string paramStr = string.Join(", ",
        parameters.Select(p => $"{p.ParameterType.Name} {p.Name}"));
    Console.WriteLine($"{method.ReturnType.Name} {method.Name}({paramStr})");
}

输出:

Void SayHello()
String ToString()
Boolean Equals(Object obj)
Int32 GetHashCode()
Type GetType()
...
// 获取特定方法
MethodInfo sayHello = type.GetMethod("SayHello");
Console.WriteLine($"找到方法: {sayHello?.Name}");

// 获取私有方法
MethodInfo secret = type.GetMethod("Secret",
    BindingFlags.NonPublic | BindingFlags.Instance);
Console.WriteLine($"私有方法: {secret?.Name}");

3.4 获取字段

Type type = typeof(Person);

// 获取所有字段(包括私有)
FieldInfo[] fields = type.GetFields(
    BindingFlags.NonPublic | BindingFlags.Instance);

foreach (FieldInfo field in fields)
{
    Console.WriteLine($"字段: {field.FieldType.Name} {field.Name}");
    Console.WriteLine($"  私有: {field.IsPrivate}");
}

输出:

字段: Int32 _age
  私有: True

3.5 完整扫描示例

using System;
using System.Reflection;

class Program
{
    static void ScanType(Type type)
    {
        Console.WriteLine($"\n===== {type.Name} 全扫描 =====");
        Console.WriteLine($"全名: {type.FullName}");
        Console.WriteLine($"基类: {type.BaseType?.Name}");

        // 构造函数
        Console.WriteLine($"\n--- 构造函数 ({type.GetConstructors().Length}个) ---");
        foreach (var ctor in type.GetConstructors())
        {
            var p = string.Join(", ", ctor.GetParameters().Select(x => $"{x.ParameterType.Name} {x.Name}"));
            Console.WriteLine($"  {type.Name}({p})");
        }

        // 属性
        var props = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        Console.WriteLine($"\n--- 属性 ({props.Length}个) ---");
        foreach (var prop in props)
        {
            string access = (prop.CanRead ? "get;" : "") + (prop.CanWrite ? "set;" : "");
            string visibility = prop.GetMethod?.IsPublic == true ? "public" : "private";
            Console.WriteLine($"  {visibility} {prop.PropertyType.Name} {prop.Name} {{ {access} }}");
        }

        // 方法
        var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic
            | BindingFlags.Instance | BindingFlags.DeclaredOnly);
        Console.WriteLine($"\n--- 方法 ({methods.Length}个) ---");
        foreach (var method in methods)
        {
            var p = string.Join(", ", method.GetParameters().Select(x => $"{x.ParameterType.Name} {x.Name}"));
            string visibility = method.IsPublic ? "public" : "private";
            Console.WriteLine($"  {visibility} {method.ReturnType.Name} {method.Name}({p})");
        }

        // 字段
        var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        Console.WriteLine($"\n--- 字段 ({fields.Length}个) ---");
        foreach (var field in fields)
        {
            string visibility = field.IsPublic ? "public" : "private";
            Console.WriteLine($"  {visibility} {field.FieldType.Name} {field.Name}");
        }
    }

    static void Main()
    {
        ScanType(typeof(Person));
    }
}

四、动态创建对象和调用

4.1 动态创建对象

Type type = typeof(Person);

// 方式一:Activator.CreateInstance(无参构造)
object obj1 = Activator.CreateInstance(type);
Console.WriteLine($"类型: {obj1.GetType().Name}");

// 方式二:指定构造函数参数
object obj2 = Activator.CreateInstance(type, "张三");
Console.WriteLine($"Name: {((Person)obj2).Name}");  // 张三

// 方式三:拿到构造函数再调用
ConstructorInfo ctor = type.GetConstructor(new[] { typeof(string) });
object obj3 = ctor.Invoke(new object[] { "李四" });

// 泛型版本
Person p = Activator.CreateInstance<Person>();

4.2 动态设置属性

Type type = typeof(Person);
object obj = Activator.CreateInstance(type);

// 获取属性
PropertyInfo nameProp = type.GetProperty("Name");

// 设置属性值
nameProp.SetValue(obj, "张三");
Console.WriteLine(nameProp.GetValue(obj));  // 张三

// 动态获取和设置——完整示例
static void SetProperty(object obj, string propName, object value)
{
    Type type = obj.GetType();
    PropertyInfo prop = type.GetProperty(propName);
    if (prop != null && prop.CanWrite)
    {
        prop.SetValue(obj, value);
        Console.WriteLine($"设置 {propName} = {value}");
    }
}

static object GetProperty(object obj, string propName)
{
    Type type = obj.GetType();
    PropertyInfo prop = type.GetProperty(propName);
    return prop?.GetValue(obj);
}

4.3 动态调用方法

Type type = typeof(Person);
object obj = Activator.CreateInstance(type);

// 设置 Name
type.GetProperty("Name")?.SetValue(obj, "张三");

// 调用无参方法
MethodInfo sayHello = type.GetMethod("SayHello");
sayHello.Invoke(obj, null);  // 输出: 你好,我是张三

// 调用有参方法
MethodInfo setName = type.GetMethod("set_Name");  // 属性的 set 方法
setName.Invoke(obj, new object[] { "李四" });

// 调用私有方法
MethodInfo secret = type.GetMethod("Secret",
    BindingFlags.NonPublic | BindingFlags.Instance);
secret.Invoke(obj, null);  // 输出: 秘密方法

4.4 动态操作字段

Type type = typeof(Person);
object obj = Activator.CreateInstance(type);

// 获取私有字段
FieldInfo ageField = type.GetField("_age",
    BindingFlags.NonPublic | BindingFlags.Instance);

// 设置私有字段
ageField.SetValue(obj, 25);

// 读取私有字段
int age = (int)ageField.GetValue(obj);
Console.WriteLine($"年龄: {age}");  // 25

五、完整的反射使用示例

5.1 通用对象拷贝工具

using System;
using System.Reflection;

public class ObjectCopier
{
    // 将一个对象的属性值拷贝到另一个对象
    public static void Copy<TSource, TTarget>(TSource source, TTarget target)
    {
        Type sourceType = typeof(TSource);
        Type targetType = typeof(TTarget);

        foreach (PropertyInfo sourceProp in sourceType.GetProperties())
        {
            if (!sourceProp.CanRead) continue;

            PropertyInfo targetProp = targetType.GetProperty(sourceProp.Name);
            if (targetProp == null || !targetProp.CanWrite) continue;

            // 类型必须兼容
            if (targetProp.PropertyType.IsAssignableFrom(sourceProp.PropertyType))
            {
                object value = sourceProp.GetValue(source);
                targetProp.SetValue(target, value);
            }
        }
    }
}

// 使用
public class StudentDto
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public class StudentEntity
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Grade { get; set; }  // 这个不会被拷贝
}

class Program
{
    static void Main()
    {
        var dto = new StudentDto { Name = "张三", Age = 18 };
        var entity = new StudentEntity();

        ObjectCopier.Copy(dto, entity);

        Console.WriteLine($"Name: {entity.Name}");   // 张三
        Console.WriteLine($"Age: {entity.Age}");      // 18
        Console.WriteLine($"Grade: {entity.Grade}");  // null(没被拷贝)
    }
}

5.2 简单的依赖注入容器

using System;
using System.Collections.Generic;
using System.Reflection;

public class SimpleContainer
{
    private Dictionary<Type, Type> _mappings = new Dictionary<Type, Type>();

    // 注册接口和实现类的映射
    public void Register<TInterface, TImplementation>()
    {
        _mappings[typeof(TInterface)] = typeof(TImplementation);
    }

    // 解析——创建对象并自动注入依赖
    public T Resolve<T>()
    {
        return (T)Resolve(typeof(T));
    }

    private object Resolve(Type type)
    {
        // 如果注册了映射,就用实现类
        if (_mappings.ContainsKey(type))
        {
            type = _mappings[type];
        }

        // 找到构造函数(选参数最多的那个)
        var constructors = type.GetConstructors();
        if (constructors.Length == 0)
        {
            return Activator.CreateInstance(type);
        }

        var ctor = constructors[0];
        var parameters = ctor.GetParameters();
        var args = new object[parameters.Length];

        // 递归解析每个参数
        for (int i = 0; i < parameters.Length; i++)
        {
            args[i] = Resolve(parameters[i].ParameterType);
        }

        return ctor.Invoke(args);
    }
}


// ===== 使用示例 =====
public interface ILogger
{
    void Log(string message);
}

public class ConsoleLogger : ILogger
{
    public void Log(string message) => Console.WriteLine($"[日志] {message}");
}

public interface IDataService
{
    void Save(string data);
}

public class DataService : IDataService
{
    private ILogger _logger;

    // 构造函数注入
    public DataService(ILogger logger)
    {
        _logger = logger;
    }

    public void Save(string data)
    {
        _logger.Log($"保存数据: {data}");
        Console.WriteLine("数据已保存");
    }
}

class Program
{
    static void Main()
    {
        Console.WriteLine("===== 简单依赖注入容器 — 反射演示 =====\n");

        var container = new SimpleContainer();
        container.Register<ILogger, ConsoleLogger>();
        container.Register<IDataService, DataService>();

        var service = container.Resolve<IDataService>();
        service.Save("用户信息");

        // 输出:
        // [日志] 保存数据: 用户信息
        // 数据已保存
    }
}

5.3 动态调用——简易插件系统

using System;
using System.Reflection;

// 插件接口
public interface IPlugin
{
    string Name { get; }
    void Execute();
}

// 内置插件
public class HelloPlugin : IPlugin
{
    public string Name => "Hello插件";
    public void Execute() => Console.WriteLine("Hello World!");
}

public class TimePlugin : IPlugin
{
    public string Name => "时间插件";
    public void Execute() => Console.WriteLine($"现在时间: {DateTime.Now}");
}

class Program
{
    static void Main()
    {
        Console.WriteLine("===== 简易插件系统 — 反射演示 =====\n");

        // 扫描程序集,找到所有 IPlugin 的实现
        Assembly assembly = Assembly.GetExecutingAssembly();
        Type pluginInterface = typeof(IPlugin);

        var pluginTypes = assembly.GetTypes()
            .Where(t => pluginInterface.IsAssignableFrom(t) && t != pluginInterface);

        Console.WriteLine($"找到 {pluginTypes.Count()} 个插件:\n");

        foreach (Type type in pluginTypes)
        {
            // 动态创建插件实例
            IPlugin plugin = (IPlugin)Activator.CreateInstance(type);
            Console.WriteLine($"  📦 {plugin.Name}");
            Console.Write($"     ");
            plugin.Execute();
            Console.WriteLine();
        }
    }
}

输出:

===== 简易插件系统 — 反射演示 =====

找到 2 个插件:

  📦 Hello插件
     Hello World!

  📦 时间插件
     现在时间: 2026-06-29 14:30:00

六、Assembly —— 程序集的反射

// 获取当前程序集
Assembly assembly = Assembly.GetExecutingAssembly();

// 获取程序集中的所有类型
Type[] allTypes = assembly.GetTypes();
Console.WriteLine($"程序集包含 {allTypes.Length} 个类型:");
foreach (Type type in allTypes.Take(5))  // 只显示前 5 个
{
    Console.WriteLine($"  {type.FullName}");
}

// 从文件加载程序集
Assembly externalAssembly = Assembly.LoadFrom("D:\\MyLibrary.dll");
Type[] externalTypes = externalAssembly.GetTypes();

// 获取程序集信息
Console.WriteLine($"\n程序集名称: {assembly.GetName().Name}");
Console.WriteLine($"版本: {assembly.GetName().Version}");
Console.WriteLine($"位置: {assembly.Location}");

七、BindingFlags —— 控制反射搜索范围

// BindingFlags 决定了反射能找到什么样的成员

// 只看 public 的(默认)
type.GetMethods();  // 等价于 type.GetMethods(BindingFlags.Public | BindingFlags.Instance)

// 看 public + private
type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

// 只看到静态的
type.GetMethods(BindingFlags.Public | BindingFlags.Static);

// 全看——public + private + static + instance
type.GetMethods(
    BindingFlags.Public | BindingFlags.NonPublic |
    BindingFlags.Static | BindingFlags.Instance);
BindingFlags 含义
Public 公开成员
NonPublic 私有、受保护成员
Static 静态成员
Instance 实例成员
DeclaredOnly 只看本类定义的,不看继承的

如果要用 NonPublic 或 Static,必须同时明确指定 Public 或 Instance、NonPublic 等。
只写 BindingFlags.NonPublic 是找不到任何东西的!


八、常见易错点(避坑指南)

坑1:BindingFlags 必须组合使用

// ❌ 只写 NonPublic,找不到任何东西!
var methods = type.GetMethods(BindingFlags.NonPublic);  // 返回空!

// ✅ 必须同时指定 NonPublic 和 Instance(或 Static)
var methods = type.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance);

坑2:反射有性能开销

// ❌ 频繁反射——每次都要走元数据
for (int i = 0; i < 10000; i++)
{
    Type type = typeof(Person);
    PropertyInfo prop = type.GetProperty("Name");
    prop.SetValue(obj, "张三");
}

// ✅ 缓存 MemberInfo 对象
PropertyInfo prop = typeof(Person).GetProperty("Name");  // 只获取一次
for (int i = 0; i < 10000; i++)
{
    prop.SetValue(obj, "张三");  // 用缓存的 prop
}

坑3:GetType 找不到类型

// Type.GetType 通过字符串找类型时,必须包含命名空间
Type t1 = Type.GetType("Person");             // ❌ 找不到!缺少命名空间
Type t2 = Type.GetType("MyApp.Models.Person"); // ✅ 完整命名空间

// 如果是顶级类用 "Program+Person" 格式
// 推荐用 typeof,安全且编译时检查
Type t3 = typeof(Person);  // ✅

坑4:反射调用方法时参数不匹配

// 假设方法签名是: void SetInfo(string name, int age)
MethodInfo method = type.GetMethod("SetInfo");

// ❌ 参数类型不匹配
// method.Invoke(obj, new object[] { "张三", "18" });  // 18 是 string,不是 int

// ✅ 正确
method.Invoke(obj, new object[] { "张三", 18 });

坑5:修改私有成员——可以做到但慎用

// 反射可以突破封装,访问私有成员
FieldInfo privateField = typeof(SecretClass).GetField("_password",
    BindingFlags.NonPublic | BindingFlags.Instance);
privateField.SetValue(obj, "新密码");  // 能改,但破坏了封装

// 仅在测试或特殊场景使用,不要滥用

坑6:Invoke 方法比直接调用慢约 100~1000 倍

// 调用次数多的话,考虑用委托缓存加速
MethodInfo method = typeof(Calculator).GetMethod("Add");
var addDelegate = (Func<Calculator, int, int, int>)Delegate.CreateDelegate(
    typeof(Func<Calculator, int, int, int>), method);

// 以后就调用委托,比 Invoke 快很多
int result = addDelegate(calc, 3, 5);

九、总结

反射能做什么

能力 核心技术
获取类型信息 typeof(T) / obj.GetType() / Type.GetType()
查看成员列表 GetProperties(), GetMethods(), GetFields()
创建对象 Activator.CreateInstance(type)
调用方法 MethodInfo.Invoke(obj, args)
读写属性 PropertyInfo.SetValue() / GetValue()
读写字段 FieldInfo.SetValue() / GetValue()
调用私有成员 BindingFlags.NonPublic

反射的应用场景

场景 说明
ORM 框架 Entity Framework、Dapper 通过反射将数据库行映射到对象
依赖注入 框架自动分析构造函数参数并创建对象
序列化 JSON/XML 序列化器通过反射读写属性和字段
单元测试 测试框架通过反射找到 [TestMethod] 标记的方法
特性读取 通过反射获取 [Attribute] 信息
插件系统 动态加载外部 dll 并创建对象

记忆口诀

反射就像 X 光机,照一照类就知道
属性方法都看见,私有的也藏不了

typeof 获取静态型,GetType 实例来取型
Activator 创对象,Invoke 调用方法名

GetProperties 看属性,GetMethods 看方法
GetFields 看字段,SetValue 给它们赋值

性能开销要注意,多缓存来少反射
BindingFlags 别忘组合,私有静态要写明

一句话总结:反射是 C# 运行时查看和操作类型的机制——通过 Type 对象获取类的所有信息,通过 Activator 动态创建对象,通过 MethodInfo/PropertyInfo/FieldInfo 动态调用、读写成员。它是 ORM、依赖注入、序列化等框架的基石。性能有开销,使用时注意缓存。

0
博主关闭了当前页面的评论