文章目录
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运算符
// typeof(x)中的x,必须是具体的类名、类型名称等,不可以是变量名称。
Type t = typeof(int);
- 使用GetType()方法
// GetType()方法继承自Object,所以C#中任何对象都具有GetType()方法
int i;
Type t = i.GetType();
- 使用Type类的静态方法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 指定反射查找范围
默认值:
// 默认值
Default
查找:
这些标记用于反射的时候查找类型成员:
// 表示查找的时候,需要忽略大小写。
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.使用案例
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);
结果
18
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.");
结果
Parsing successful: 123
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();
结果
ming111 say: Hello
6.4.2 用Activator生成对象
object obj1 = Activator.CreateInstance(peopleType, "ming222", 25);
((People)obj1).SayHello();
结果
ming222 say: Hello
7.反射总结
- 优点:
反射是C#中一个非常强大的特性是C#高级编程中不可或缺的一部分,了解和掌握反射的使用可以帮助开发者编写更加灵活和强大的.NET应用程序。它提供了一种在运行时查询和操作类型信息的能力,通过反射,我们可以动态地创建对象、调用方法、访问字段和属性,这为编写灵活和动态的代码提供了极大的便利。 - 缺点:
尽管反射提供了强大的功能,但它也有一些缺点。反射操作通常比直接代码调用要慢,因为它需要在运行时解析类型信息。此外,过度使用反射可能会使代码变得难以理解和维护。因此,我们应该谨慎使用,在使用反射时应该权衡其给项目带来的好处和成本,避免不必要的性能开销和复杂性增加。