C#反射

C# 反射使用指南

1. 反射概念

  • 元数据(Metadata):程序及类型的相关信息,如类、方法、字段、属性等,存储在程序集(.dll 或 .exe)中。
  • 反射(Reflection):程序在运行时获取自身或其他程序集的元数据,并可动态实例化对象、访问成员、调用方法。
  • 程序集(Assembly):.NET 的代码打包单位,分为 DLL(库)和 EXE(可执行文件)。

2. 获取类型信息(Type)

2.1 获取 Type 的三种方式

int a = 1;

// 1. 对象实例的 GetType()
Type t1 = a.GetType();

// 2. typeof 关键字
Type t2 = typeof(int);

// 3. Type.GetType(string) 通过字符串获取
Type t3 = Type.GetType("System.Int32")!;

注意:Type.GetType(string) 默认只在当前程序集和系统程序集搜索类型,跨程序集可能返回 null。

2.2 Type 常用属性

Type t = typeof(Test);

Console.WriteLine(t.Assembly);  // 获取类型所属程序集
Console.WriteLine(t.Namespace); // 获取命名空间
Console.WriteLine(t.Name);      // 获取类名

3. 获取类成员

3.1 获取公共成员

Type t = typeof(Test);
MemberInfo[] members = t.GetMembers();  // 获取所有公共成员
foreach (var m in members)
    Console.WriteLine(m);

⚡ 默认只获取 公共实例成员,无需额外指定 BindingFlags

3.2 获取字段(FieldInfo)

// 获取公共或非公共实例字段,需要指定 BindingFlags
FieldInfo field = t.GetField("name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

// 设置字段值(实例字段)
field.SetValue(obj, "Alice");

// 获取字段值(实例字段)
Console.WriteLine(field.GetValue(obj));

// 静态字段
FieldInfo staticField = t.GetField("StaticValue", BindingFlags.Static | BindingFlags.Public);
staticField.SetValue(null, 100);
Console.WriteLine(staticField.GetValue(null));

GetValue/SetValue 参数规则

  • 实例字段:传入对象实例
  • 静态字段:传入 null

3.3 获取方法(MethodInfo)

// 公共实例方法(可省略 BindingFlags)
MethodInfo method = t.GetMethod("Add", new Type[]{ typeof(int) });
method.Invoke(obj, new object[]{ 10 });  // 调用实例方法

// 静态方法必须指定 Static 或直接传 null
MethodInfo staticMethod = t.GetMethod("StaticAdd", BindingFlags.Static | BindingFlags.Public);
staticMethod.Invoke(null, new object[]{ 5 });

3.4 获取构造函数(ConstructorInfo)

// 公共无参构造
ConstructorInfo ctor = t.GetConstructor(Type.EmptyTypes);
object obj = ctor.Invoke(null);

// 私有无参构造
ConstructorInfo privateCtor = t.GetConstructor(
    BindingFlags.Instance | BindingFlags.NonPublic,
    null,
    Type.EmptyTypes,
    null
);
object obj2 = privateCtor.Invoke(null);

// 使用 Activator
object obj3 = Activator.CreateInstance(t, true); // true:允许反射私有构造函数

建议:尽量使用 Activator.CreateInstance,支持私有构造函数,代码简洁。


4. BindingFlags 使用注意

  • 默认只能找到 公共实例成员
  • 必须指定 Instance 或 Static 才能获取非默认成员
  • 常用组合:
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static
Flag 作用
Public 查找公共成员
NonPublic 查找非公共成员(private/protected)
Instance 查找实例成员
Static 查找静态成员

4.1 省略 BindingFlags 的场景

场景 是否可省略 BindingFlags 说明
公共实例成员 ✅ 可省略 默认返回公共实例成员,如 GetMethod("Add")
公共静态成员 ❌ 不可省略 必须加 Static
非公共成员(私有/保护) ❌ 不可省略 必须加 NonPublic + Instance/Static
GetProperties / GetFields 默认 ✅ 返回公共实例成员 若要获取私有或静态字段,需要明确指定 BindingFlags

⚡ 结论:公共实例成员可省略 BindingFlags,其余情况必须指定


5. 动态实例化对象(Activator)

Type t = typeof(T);

// 无参构造
T obj1 = (T)Activator.CreateInstance(t);

// 有参构造
T obj2 = (T)Activator.CreateInstance(t, 99, "Hello");

// 支持私有构造函数
T obj3 = (T)Activator.CreateInstance(t, true);

//语法糖 仅支持无参构造 没有重载
T obj1 = Activator.CreateInstance<T>()

⚡ Activator.CreateInstance 优势:

  • 简化 ConstructorInfo 调用
  • 支持私有构造函数
  • 可直接传入构造函数参数

6. 跨程序集反射

6.1 问题

Type t = Type.GetType("BNameSpace.ClassName"); // 返回 null
  • 原因:Type.GetType(string) 默认只在 当前程序集 或系统程序集查找,找不到外部 DLL 的类型。

6.2 解决方案

// 1. 先加载目标程序集
Assembly asm = Assembly.LoadFrom("B.dll");

// 2. 从程序集获取类型
Type t = asm.GetType("BNameSpace.ClassName");

// 3. 调用构造函数、方法等
object obj = Activator.CreateInstance(t, 1, "abc")!;
MethodInfo method = t.GetMethod("DoSomething")!;
method.Invoke(obj, new object[]{ 10 });

6.3 使用 typeof 的跨程序集方式

  • typeof(B.Class) 编译时会检查类型存在,需要添加对 B.dll 的引用
  • 编译通过 → 运行时安全可靠

7. 总结反射使用规范

  1. 优先使用 typeof 或对象实例的 GetType(),安全可靠。

  2. 跨程序集反射,优先使用 Assembly.LoadAssembly.LoadFrom + Assembly.GetType

  3. 访问非公共成员,必须指定 BindingFlags.NonPublic 和 Instance/Static。

  4. 动态实例化对象推荐使用 Activator.CreateInstance,支持私有构造函数。

  5. 慎用 Type.GetType(string),容易返回 null,适合反射系统程序集类型。

  6. 公共实例成员可以省略 BindingFlags,静态或私有成员必须指定。

  7. FieldInfo.GetValue/SetValue

    • 实例字段 → 传对象实例
    • 静态字段 → 传 null
  8. PropertyInfo.GetValue/SetValue 遵循属性 getter/setter,静态属性第一个参数传 null


8. 推荐反射操作流程

获取类型

Type t = typeof(MyClass); // 或 Assembly.GetType("Namespace.ClassName")

获取成员

// 公共或非公共实例字段
FieldInfo f = t.GetField("name", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

// 公共实例方法
MethodInfo m = t.GetMethod("Add", new Type[]{ typeof(int) });

// 构造函数
ConstructorInfo ctor = t.GetConstructor(Type.EmptyTypes);

创建对象

object obj = ctor.Invoke(null);

调用方法 / 访问字段

// 调用方法
m.Invoke(obj, new object[]{ 10 });

// 访问字段
f.SetValue(obj, "Alice");

⚡ 补充:

  • 静态字段或方法 → GetValue/SetValue / Invoke 第一个参数传 null
  • 公共实例成员 → 可省略 BindingFlags
posted @ 2025-11-23 11:27  高山仰止666  阅读(0)  评论(0)    收藏  举报