写在前面的话
我创建的Unity、C#交流群,有兴趣可加入大家一起学习:952914223
0.应用程序结构
应用程序结构:
1.反射概念
反射是.NET框架提供的一个功能强大的机制,它允许程序在运行时检查和操作对象的类型信息。通过使用反射,程序可以动态地创建对象、调用方法、访问字段和属性,无需在编译时显式知道类型信息。在.NET中,所有类型的信息最终都是存储在元数据中的。反射就是.NET提供的一组API,允许我们在运行时访问这些元数据,从而获得关于程序集、模块、类型、成员等的详细信息。
反射概念图:
2.反射用途
System.Reflection 命名空间下
| 类 | 作用 |
|---|
| Assembly | 定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例。 | | MethodInfo | 了解方法的名称、返回类型、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法来调用特定的方法。 | | PropertyInfo | 了解属性的名称、数据类型、声明类型、反射类型和只读或可写状态等,获取或设置属性值。 | | FieldInfo | 了解字段的名称、访问修饰符(如public或private)和实现详细信息(如static)等,并获取或设置字段值。 | | MemberInfo | 用于获取有关类 (构造函数、事件、字段、方法和属性) 的所有成员的信息。此类引入所有成员提供的基本功能。 | | EventInfo | 了解事件的名称、事件处理程序数据类型、自定义属性、声明类型和反射类型等,添加或移除事件处理程序。 | | ConstructorInfo | 了解构造函数的名称、参数、访问修饰符(如pulic 或private)和实现详细信息(如abstract或virtual)等。使用Type的GetConstructors或 GetConstructor方法来调用特定的构造函数。 | | ParameterInfo | 了解参数的名称、数据类型、是输入参数还是输出参数,以及参数在方法签名中的位置等。 |
3.System.Type 类
通过这个类可以访问任何给定数据类型的信息。 System.Type 类对于反射起着核心的作用。但它是一个抽象的基类,Type有与每种数据类型对应的派生类,我们使用这个派生类的对象的方法、字段、属性来查找有关该类型的所有信息。 生成Type对象:
- // typeof(x)中的x,必须是具体的类名、类型名称等,不可以是变量名称。
- Type t = typeof(int);
复制代码
- // GetType()方法继承自Object,所以C#中任何对象都具有GetType()方法
- int i;
- Type t = i.GetType();
复制代码
- Type t = Type.GetType("System.Double");
复制代码
属性:
| 属性 | 描述 |
|---|
| Name | 数据类型名 | | FullName | 数据类型的完全限定名(包括命名空间名) | | Namespace | 定义数据类型的命名空间名 | | IsAbstract | 指示该类型是否是抽象类型 | | IsArray | 指示该类型是否是数组 | | IsClass | 指示该类型是否是类 | | IsEnum | 指示该类型是否是枚举 | | IsInterface | 指示该类型是否是接口 | | IsPublic | 指示该类型是否是公有的 | | IsSealed | 指示该类型是否是密封类 | | IsValueType | 指示该类型是否是值类型 |
方法:
| 方法 | 描述 |
|---|
| GetConstructor(),GetConstructors() | 返回ConstructorInfo类型,用于取得该类的构造函数的信息 | | GetEvent(), GetEvents() | 返回EventInfo类型,用于取得该类的事件的信息 | | GetField(), GetFields() | 返回FieldInfo类型,用于取得该类的字段(成员变量)的信息 | | GetInterface(),GetInterfaces() | 返回InterfaceInfo类型,用于取得该类实现的接口的信息 | | GetMember(), GetMembers() | 返回MemberInfo类型,用于取得该类的所有成员的信息 | | GetMethod(), GetMethods() | 返回MethodInfo类型,用于取得该类的方法的信息 | | GetProperty(), GetProperties() | 返回PropertyInfo类型,用于取得该类的属性的信息 |
4.System.Reflection.Assembly 类
| 方法 | 描述 |
|---|
| Load(String) | 通过程序集名称返回Assembly对象 | | LoadFrom(String) | 通过DLL文件名称返回Assembly对象 | | LoadFile(String) | 加载指定路径上的程序集文件的内容返回Assembly对象。 | | GetType(String) | 通过Assembly获取程序集中类,参数必须是类的全名 | | GetTypes | 通过Assembly获取程序集中所有的类 |
- // 通过程序集名称返回Assembly对象
- Assembly ass = Assembly.Load("MyClassLibrary");
- // 通过DLL文件名称返回Assembly对象
- Assembly ass = Assembly.LoadFrom("MyClassLibrary.dll");
- // 通过Assembly获取程序集中类
- Type t = ass.GetType("MyClassLibrary.NewClass"); //参数必须是类的全名
- // 通过Assembly获取程序集中所有的类
- Type[] t = ass.GetTypes();
复制代码
5.BindingFlags 指定反射查找范围
默认值:
查找: 这些标记用于反射的时候查找类型成员: - // 表示查找的时候,需要忽略大小写。
- IgnoreCase
- // 仅查找此特定类型中声明的成员,而不会包括这个类继承得到的成员。
- DeclaredOnly
- // 仅查找类型中的实例成员。
- Instance
- // 仅查找类型中的静态成员。
- Static
- // 仅查找类型中的公共成员。
- Public
- // 仅查找类型中的非公共成员(internal protected private)
- NonPublic
- // 会查找此特定类型继承树上得到的静态成员。但仅继承公共(public)静态成员和受保护(protected)静态成员;不包含私有静态成员,也不包含嵌套类型。
- FlattenHierarchy
复制代码
调用: 这些标记用于为InvokeMember方法提供参数,告知应该如何反射调用一个方法: - // 调用方法。
- InvokeMethod
- // 创建实例。
- CreateInstance
- // 获取字段的值。
- GetField
- // 设置字段的值。
- SetField
- // 获取属性的值。
- GetProperty
- // 设置属性的值。
- SetProperty
复制代码
常用的组合:
- BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance
复制代码
- BindingFlags.Public | BindingFlags.Instance
复制代码
6.使用案例
People 类: - namespace Ming
- {
- public class People
- {
- string name;
- int age;
-
- public string Name
- {
- get => name;
- set => name = value;
- }
-
- public int Age
- {
- get => age;
- set => age = value;
- }
-
- public People(string name, int age)
- {
- this.name = name;
- this.age = age;
- }
-
- public void SayHello()
- {
- Console.Writeline($"{name} say:Hello");
- }
- }
- }
复制代码
6.1 获取类的名字、命名空间、程序集 - //类的名称
- string name = peopleType.Name;
- Console.WriteLine(name);
- //类的命名空间
- string space = peopleType.Namespace;
- Console.WriteLine(space);
- //类的程序集
- Assembly assembly = peopleType.Assembly;
- Console.WriteLine(assembly);
- /*
- People
- Ming
- DesignPattern, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
- */
复制代码
6.2 获取/修改类的字段、属性、方法、构造函数、事件等 - BindingFlags flag = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;
- People people = new People("ming", 18);
- // 获取Type对象
- Type peopleType = people.GetType();
- // 获取name字段值
- Console.WriteLine("------获取name字段值------");
- FieldInfo nameField = peopleType.GetField("name", flag);
- Console.WriteLine(nameField.GetValue(people));
- // 设置name字段
- Console.WriteLine("------设置name字段------");
- nameField.SetValue(people, "ming123");
- Console.WriteLine(nameField.GetValue(people));
- // 获取所有字段
- Console.WriteLine("------获取所有字段------");
- FieldInfo[] fieldInfos = peopleType.GetFields(flag);
- foreach (var field in fieldInfos)
- {
- Console.WriteLine($"{field.Name}: {field.GetValue(people)}");
- }
- // 获取所有属性
- Console.WriteLine("------获取所有属性------");
- PropertyInfo[] propertyInfos = peopleType.GetProperties(flag);
- foreach (var property in propertyInfos)
- {
- Console.WriteLine($"{property.Name}: {property.GetValue(people)}");
- }
- // 获取所有方法
- Console.WriteLine("------获取所有方法------");
- MethodInfo[] methodInfos = peopleType.GetMethods(BindingFlags.Instance | BindingFlags.Public);
- foreach (var method in methodInfos)
- {
- Console.WriteLine($"{method.Name}");
- }
- // 查看类构造函数
- ConstructorInfo[] constructorInfos = peopleType.GetConstructors();
- foreach (ConstructorInfo constructor in constructorInfos)
- {
- ParameterInfo[] ps = constructor.GetParameters(); //取出每个构造函数的所有参数
- foreach (ParameterInfo pi in ps)
- {
- Console.Write(pi.ParameterType.ToString() + " " + pi.Name + ",");
- }
- }
- /*
- ------获取name字段值------
- ming
- ------设置name字段------
- ming123
- ------获取所有字段------
- name: ming123
- age: 18
- ------获取所有属性------
- Name: ming123
- Age: 18
- ------获取所有方法------
- get_Name
- set_Name
- get_Age
- set_Age
- SayHello
- GetType
- ToString
- Equals
- GetHashCode
- ------查看类构造函数------
- System.String name,System.Int32 age,
- */
复制代码
6.3 方法调用
6.3.1 调用无参函数 - // 调用无参方法
- MethodInfo method1 = peopleType.GetMethod("SayHello");
- Console.WriteLine($"方法名:{method1.Name}");
- Console.WriteLine($"返回值:{method1.ReturnType}");
- method1.Invoke(people, null);
- /*
- 方法名:SayHello
- 返回值:System.Void
- ming123 say: Hello
- */
复制代码
6.3.2 调用有参函数
People 新增方法 - public int Sum(int a, int b)
- {
- return a + b;
- }
复制代码
调用 - MethodInfo method2 = peopleType.GetMethod("Sum");
- Console.WriteLine($"方法名:{method2.Name}");
- Console.WriteLine($"返回值:{method2.ReturnType}");
- foreach (var item in method2.GetParameters())
- {
- Console.WriteLine($"参数名:{item.Name}");
- Console.WriteLine($"参数类型:{item.ParameterType}");
- }
- var sum = method2.Invoke(people, new object[]{1, 2});
- Console.WriteLine(sum);
复制代码
结果 - 方法名:Sum
- 返回值:System.Int32
- 参数名:a
- 参数类型:System.Int32
- 参数名:b
- 参数类型:System.Int32
- 3
复制代码
6.3.3 调用私有方法
People 新增方法 - private void PrintAge()
- {
- Console.WriteLine(age);
- }
复制代码
调用 - MethodInfo method3 = peopleType.GetMethod("PrintAge", BindingFlags.NonPublic | BindingFlags.Instance);
- method3.Invoke(people, null);
复制代码
结果
6.3.4 调用泛型方法People 新增方法
People 新增方法 - public void GenericsMethod<T>(T parm)
- {
- Console.WriteLine($"类型: {typeof(T)},值: {parm}");
- }
复制代码
调用 - MethodInfo method4 = peopleType.GetMethod("GenericsMethod").MakeGenericMethod(new Type[]{typeof(string)});
- MethodInfo method5 = peopleType.GetMethod("GenericsMethod").MakeGenericMethod(new Type[]{typeof(int)});
- method4.Invoke(people, new object[]{"sting泛型"});
- method5.Invoke(people, new object[]{123});
复制代码
结果 - 类型: System.String,值: sting泛型
- 类型: System.Int32,值: 123
复制代码
6.3.5 调用带有输出参数的方法(ref, out)
People 新增方法 - public bool TryParse(string input, out int result)
- {
- return int.TryParse(input, out result);
- }
复制代码
调用 - MethodInfo method6 = peopleType.GetMethod("TryParse");
- // 创建参数数组,包括输入和输出参数
- object[] parameters = new object[] { "123", null };
- // 调用方法
- var success = (bool)method6.Invoke(people, parameters);
- // 获取输出参数的值
- int parsedValue = (int)parameters[1];
- if (success)
- Console.WriteLine($"Parsing successful: {parsedValue}");
- else
- Console.WriteLine("Parsing failed.");
复制代码
结果
6.3.6 调用重载方法
People 新增方法 - public int Add(int a, int b)
- {
- return a + b;
- }
- public double Add(double a, double b)
- {
- return a + b;
- }
复制代码
调用 - // 指定要调用的重载方法的参数类型
- Type[] paramTypes1 = { typeof(int), typeof(int) };
- Type[] paramTypes2 = { typeof(double), typeof(double) };
- MethodInfo method7 = peopleType.GetMethod("Add", paramTypes1);
- MethodInfo method8 = peopleType.GetMethod("Add", paramTypes2);
- // 调用重载方法
- var result1 = method7.Invoke(people, new object[] { 10, 20 });
- var result2 = method8.Invoke(people, new object[] { 5.5, 6.5 });
- Console.WriteLine($"10 + 20 = {result1}");
- Console.WriteLine($"5.5 + 6.5 = {result2}");
复制代码
结果 - 10 + 20 = 30
- 5.5 + 6.5 = 12
复制代码
6.4 动态生成对象
6.4.1 用构造函数动态生成对象 - // 根据参数类型获取构造函数
- ConstructorInfo constructor1 = peopleType.GetConstructor(new Type[]{ typeof(string), typeof(int) });
- // 构造Object数组,作为构造函数的输入参数
- object[] obj = new object[2]{ "ming111", 22 };
- // 调用构造函数生成对象
- object o = constructor1.Invoke(obj);
- // 调用生成的对象的方法测试是否对象生成成功
- ((People)o).SayHello();
复制代码
结果
6.4.2 用Activator生成对象 - object obj1 = Activator.CreateInstance(peopleType, "ming222", 25);
- ((People)obj1).SayHello();
复制代码
结果
7.反射总结
- 优点:
反射是C#中一个非常强大的特性是C#高级编程中不可或缺的一部分,了解和掌握反射的使用可以帮助开发者编写更加灵活和强大的.NET应用程序。它提供了一种在运行时查询和操作类型信息的能力,通过反射,我们可以动态地创建对象、调用方法、访问字段和属性,这为编写灵活和动态的代码提供了极大的便利。 - 缺点:
尽管反射提供了强大的功能,但它也有一些缺点。反射操作通常比直接代码调用要慢,因为它需要在运行时解析类型信息。此外,过度使用反射可能会使代码变得难以理解和维护。因此,我们应该谨慎使用,在使用反射时应该权衡其给项目带来的好处和成本,避免不必要的性能开销和复杂性增加。
|