C# 反射使用指南
1. 反射概念
- 元数据(Metadata):程序及类型的相关信息,如类、字段、属性、方法、构造函数等,存储在程序集(.dll 或 .exe)中。
- 反射(Reflection):程序在运行时获取自身或其他程序集的元数据,并可动态实例化对象、访问成员、调用方法。
- 程序集(Assembly):.NET 的代码打包单位,分为 DLL(库)和 EXE(可执行文件)。
2. 获取类型信息(Type)
2.1 获取 Type 的三种方式
// 1. typeof 关键字(最常用)
Type t = typeof(MyClass);
// 2. Type.GetType(string)
Type t = Type.GetType("MyClass");
// 3. 对象实例的 GetType()(不常用 因为我们要反射的自定义类一般拿不到对象 系统类又没必要反射)
MyClass a = new MyClass();
Type t = a.GetType();
//当加载其它程序集类时
// typeof 参数补上命名空间 要使用using获取程序集空间(这也意味着不能动态加载 如果场景中没有该程序集空间的脚本就会报错)
Type t = typeof(OtherNamespace.OtherClass);
// Type.GetType 默认只在当前程序集和系统程序集搜索类型,跨程序集可能返回 null,要先反射其它程序集,通过其它程序集的GetType方法获取类型信息
Assembly assembly = Assembly.LoadFrom(@"C:\唐老狮Unity\C#进阶\Lesson16_练习题\bin\Debug\net8.0\Lesson16_练习题.dll");
Type t = assembly.GetType("OtherNamespace.OtherClass")
2.2 Type 常用属性
Type t = typeof(Test);
Console.WriteLine(t.Assembly); // 获取类型所属程序集
Console.WriteLine(t.Namespace); // 获取命名空间
Console.WriteLine(t.Name); // 获取类名
3. 获取类成员
3.1 获取类成员的方法
| 要获取什么 |
方法名 |
| 字段、属性、方法、构造函数 |
GetMembers |
| 字段 Field |
GetField |
| 属性 Property |
GetProperty |
| 方法 Method |
GetMethod |
| 构造函数 Constructor |
GetConstructor |
3.2 获取类成员的方法参数
获取类成员的方法都有四个参数 (BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers)
| 参数 |
说明 |
| BindingFlags (重要) |
BindingFlags,指定查找规则(实例/静态、公有/非公有) |
| Binder (默认为null) |
可选绑定器,默认传 null 就够用,控制重载匹配 |
| Type[](重要) |
构造函数参数类型数组,用于精确匹配哪一个构造函数 |
| ParameterModifier[](默认为null) |
参数修饰符,一般为 null |
BindingFlags 常用字段
| Flag |
作用 |
| BindingFlags.Public |
查找公共成员(public) |
| BindingFlags.NonPublic |
查找非公共成员(private/protected) |
| BindingFlags.Instance |
查找实例成员(非static) |
| BindingFlags.Static |
查找静态成员(static) |
省略BindingFlags, Binder, Type[], ParameterModifier[]时返回的类成员的类型
| 获取的类成员 |
默认返回成员类型 |
| MemberInfo[] members = typeof(MyClass).GetMembers()(特殊情况) |
返回所有公共成员(字段/属性/方法/构造函数),包括静态 |
| MemberInfo[] members = typeof(MyClass).GetMembers("memberName") |
返回符合memberName的公共实例成员(字段/属性/方法/构造函数),不包括静态 |
| FieldInfo field= typeof(MyClass).GetField("fieldName") |
返回符合fieldName公共实例字段,不包括静态 |
| MethodInfo methodInfo = typeof(MyClass).GetMethod("MethodName") |
返回符合MethodName公共实例方法,不包括静态 |
| ConstructorInfo constructorInfo = typeof(MyClass).GetConstructor(Type.EmptyTypes) |
返回公共无参构造函数,不包括静态 |
types 的使用
| 参数个数 |
Type[] 数组写法 |
| 无参数 |
Type.EmptyTypes |
| 一个参数 |
new Type[] { typeof(int) } |
| 两个参数 |
new Type[] { typeof(int), typeof(string) } |
3.3 获取成员
//Members 成员(字段、属性、方法、构造函数)
//获取公共成员(包含静态)
Type t = typeof(Test);
MemberInfo[] members = t.GetMembers();
//FieldInfo 字段(常用)
//获取公共实例字段
FieldInfo field = t.GetField("Value");
field.SetValue(obj, 10);
field.GetValue(obj);
// 获取公共静态字段
FieldInfo staticField = t.GetField("StaticValue", BindingFlags.Static | BindingFlags.Public);
//注意:静态字段不存在对象 因此使用null
staticField.SetValue(null, 10);
staticField.GetValue(null);
//PropertyInfo 属性(不常用)
//获取公共实例属性
PropertyInfo property = t.GetProperty("Value")
property.SetValue(obj, 10);
property.GetValue(obj);
//获取公共静态属性
PropertyInfo staticProperty = t.GetProperty("StaticValue", BindingFlags.Static | BindingFlags.Public);
staticProperty.SetValue(null, 10);
staticProperty.GetValue(null);
//MethodInfo 方法(常用)
// 获取公共实例方法
MethodInfo method = t.GetMethod("Add", new Type[]{ typeof(int) });
// 反射获取的方法必须使用Invoke调用
method.Invoke(obj, new object[]{ 10 });
// 获取公共静态方法
MethodInfo staticMethod = t.GetMethod("StaticAdd", BindingFlags.Static | BindingFlags.Public);
staticMethod.Invoke(null, new object[]{ 5 }); //调用静态方法
//ConstructorInfo 构造函数(不常用 直接使用Activator.CreateInstance创建实例更方便)
// 获取公共实例构造函数
ConstructorInfo ctor = t.GetConstructor(Type.EmptyTypes);
// 反射获取的构造函数必须使用Invoke调用
object obj = ctor.Invoke(null);
// 获取私有实例无参构造函数
ConstructorInfo pctor= t.GetConstructor(
BindingFlags.Instance | BindingFlags.NonPublic,
null,
Type.EmptyTypes,
null
);
object obj2 = pctor.Invoke(null);
4. 动态实例化对象(Activator)
Type t = typeof(T);
// 无参构造
T obj1 = (T)Activator.CreateInstance(t);
// 有参构造
T obj2 = (T)Activator.CreateInstance(t, 99, "Hello");
// 支持私有构造函数(同时存在公共(public)和私有(private)无参构造函数 时,还是优先调用公共无参构造函数)
T obj3 = (T)Activator.CreateInstance(t, true);
//语法糖 仅支持无参构造 没有重载
T obj1 = Activator.CreateInstance<T>();
6. 程序集反射(不常用)
- 如果想要使用不是自己程序集中的信息 需要先加载其它程序集 才能用Type来使用其它程序集中的信息
//三种加载程序集的函数
//方法一 一般用来加载在同一文件下的其它程序集
//Assembly assembly2 = Assembly.Load("程序集名称");
//方法二 一般用来加载不在同一文件下的其它程序集(一般使用这个)
//Assembly assembly = Assembly.LoadFrom("包含程序集清单的文件的名称或路径");
//方法三 一般用来加载不在同一文件下的其它程序集
//Assembly assembly3 =Assembly.LoadFile("要加载的文件的完全限定路径");
//1.先加载一个指定程序集
Assembly assembly = Assembly.LoadFrom(@"C:\唐老狮Unity\C#进阶\Lesson16_练习题\bin\Debug\net8.0\Lesson16_练习题.dll");
//2.再加载程序集中的一个类对象 之后才能使用反射
Type item = assembly.GetType("Lesson16_练习题.Item");
MemberInfo[] members = item.GetMembers();
for(int i = 0; i < members.Length; i++)
{
Console.WriteLine(members[i]);
}
//通过反射实例化一个item对象
object itemObj = Activator.CreateInstance(item, 1,"123123", 2);
//得到对象中的方法 通过反射
ConstructorInfo constructoritem = item.GetConstructor(new Type[] { typeof(int), typeof(string), typeof(int) });
7. 常用反射操作流程
// 获取类型
Type t = typeof(MyClass); // 或 Assembly.GetType("Namespace.ClassName")
// 获取公共实例字段
FieldInfo field = t.GetField("name");
// 获取公共实例方法
MethodInfo method = t.GetMethod("Add", new Type[]{ typeof(int) });
// 创建对象
object obj = t.Activator.CreateInstance(t);
// 调用方法
method.Invoke(obj, new object[]{ 10 });
// 访问字段
field.SetValue(obj, "Alice");
field.GetValue(obj);