反射(Reflection)是 .NET 中一项强大的功能,它允许程序在运行时动态获取类型信息、创建对象、调用方法、以及访问和修改类的成员(字段、属性等)。反射提供了极大的灵活性和可扩展性,特别适用于那些需要在运行时动态操作类型的场景。然而,反射也有性能开销,因此在使用时需要谨慎。本文将详细介绍 C# 反射的基本用法,包括如何获取类型信息、动态创建对象、动态调用方法、访问和修改字段/属性的值,以及获取程序集和模块信息。
1. 获取类型信息(字段、属性、方法等)
反射允许你在运行时获取对象的类型信息,进而访问类的字段、属性、方法等成员。你可以通过 Type
类来获取类型信息,利用该信息进行动态操作。
示例:获取字段、属性和方法信息
using System;
using System.Reflection;public class MyClass
{public int Field1 = 10;private string Field2 = "Hello";public int MyProperty { get; set; }public void MyMethod(){Console.WriteLine("Method executed!");}private void PrivateMethod(){Console.WriteLine("Private method executed!");}
}class Program
{static void Main(){MyClass obj = new MyClass();Type type = obj.GetType(); // 获取类型信息// 获取字段信息FieldInfo field1 = type.GetField("Field1");Console.WriteLine("Field1: " + field1.GetValue(obj)); // 输出:Field1: 10// 获取私有字段信息FieldInfo field2 = type.GetField("Field2", BindingFlags.NonPublic | BindingFlags.Instance);Console.WriteLine("Field2: " + field2.GetValue(obj)); // 输出:Field2: Hello// 获取属性信息PropertyInfo property = type.GetProperty("MyProperty");Console.WriteLine("MyProperty: " + property.GetValue(obj)); // 输出:MyProperty: 0 (默认值)// 获取方法信息MethodInfo method = type.GetMethod("MyMethod");method.Invoke(obj, null); // 输出:Method executed!// 获取私有方法信息MethodInfo privateMethod = type.GetMethod("PrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);privateMethod.Invoke(obj, null); // 输出:Private method executed!}
}
在上面的代码中,Type.GetType()
用来获取类的类型信息,GetField()
, GetProperty()
, GetMethod()
等方法可以帮助你获取字段、属性和方法的信息。通过 BindingFlags.NonPublic
可以访问私有成员。
2. 动态创建对象
反射使得在运行时动态创建对象成为可能。你可以通过类型信息来创建对象,甚至是使用带参数的构造函数来创建对象。
示例:动态创建对象并调用方法
using System;public class MyClass
{public MyClass() { Console.WriteLine("Constructor called!"); }public void DisplayMessage() { Console.WriteLine("Hello from MyClass!"); }
}class Program
{static void Main(){Type type = typeof(MyClass);// 动态创建对象object obj = Activator.CreateInstance(type);// 调用方法MethodInfo method = type.GetMethod("DisplayMessage");method.Invoke(obj, null); // 输出:Hello from MyClass!}
}
在这个示例中,Activator.CreateInstance()
方法用来动态创建 MyClass
类的实例,之后通过反射调用了 DisplayMessage
方法。
3. 动态调用方法
通过反射,你可以在运行时动态调用对象的公共或私有方法。即使方法的名称、参数等信息在编译时未知,也可以通过反射来动态调用。
示例:动态调用公共和私有方法
using System;
using System.Reflection;public class MyClass
{private void PrivateMethod(){Console.WriteLine("Private method called!");}public void PublicMethod(){Console.WriteLine("Public method called!");}
}class Program
{static void Main(){MyClass obj = new MyClass();Type type = obj.GetType();// 调用公共方法MethodInfo publicMethod = type.GetMethod("PublicMethod");publicMethod.Invoke(obj, null); // 输出:Public method called!// 调用私有方法MethodInfo privateMethod = type.GetMethod("PrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);privateMethod.Invoke(obj, null); // 输出:Private method called!}
}
在上面的代码中,我们使用 MethodInfo.Invoke()
方法动态调用了公共方法 PublicMethod
和私有方法 PrivateMethod
。
4. 访问和修改字段、属性的值
反射不仅能够读取对象的字段和属性,还能修改它们的值。你可以通过反射动态地访问和修改字段或属性,无论它们是公共的还是私有的。
示例:访问和修改字段、属性的值
using System;
using System.Reflection;public class MyClass
{public int PublicField = 100;private string PrivateField = "Hello";public int MyProperty { get; set; }
}class Program
{static void Main(){MyClass obj = new MyClass();Type type = obj.GetType();// 获取和修改字段值FieldInfo publicField = type.GetField("PublicField");Console.WriteLine("PublicField: " + publicField.GetValue(obj)); // 输出:PublicField: 100publicField.SetValue(obj, 200);Console.WriteLine("PublicField (after modification): " + publicField.GetValue(obj)); // 输出:PublicField (after modification): 200// 获取私有字段值FieldInfo privateField = type.GetField("PrivateField", BindingFlags.NonPublic | BindingFlags.Instance);Console.WriteLine("PrivateField: " + privateField.GetValue(obj)); // 输出:PrivateField: HelloprivateField.SetValue(obj, "New Value");Console.WriteLine("PrivateField (after modification): " + privateField.GetValue(obj)); // 输出:PrivateField (after modification): New Value// 获取和修改属性值PropertyInfo property = type.GetProperty("MyProperty");property.SetValue(obj, 50);Console.WriteLine("MyProperty: " + property.GetValue(obj)); // 输出:MyProperty: 50}
}
在这个示例中,FieldInfo.GetValue()
和 PropertyInfo.GetValue()
方法用于读取字段或属性的值,FieldInfo.SetValue()
和 PropertyInfo.SetValue()
方法则用于修改字段或属性的值。
5. 获取程序集和模块信息
反射还可以用于获取当前程序集中所有的类型、方法、属性等信息。程序集(Assembly)是包含类型定义的容器,而模块(Module)是程序集中的组成部分。通过反射,可以获取程序集和模块的信息。
示例:获取程序集和模块信息
using System;
using System.Reflection;class Program
{static void Main(){// 获取当前程序集Assembly assembly = Assembly.GetExecutingAssembly();Console.WriteLine("Assembly: " + assembly.FullName);// 获取程序集中的所有类型Console.WriteLine("\nTypes in Assembly:");foreach (Type type in assembly.GetTypes()){Console.WriteLine(type.FullName);}// 获取模块信息Console.WriteLine("\nModules in Assembly:");foreach (Module module in assembly.GetModules()){Console.WriteLine("Module: " + module.Name);}}
}
在这个示例中,使用 Assembly.GetExecutingAssembly()
获取当前程序的程序集信息,通过 GetTypes()
方法列出程序集中的所有类型,GetModules()
获取程序集中的模块信息。
6. 总结
反射是 C# 中非常强大且灵活的功能,它允许程序在运行时动态获取类型信息、创建对象、调用方法、访问和修改字段及属性的值。常见的反射用途包括:
- 获取类型信息:通过
Type
获取类的字段、属性、方法等信息。 - 动态创建对象:使用
Activator.CreateInstance()
动态创建对象。 - 动态调用方法:通过
MethodInfo.Invoke()
动态调用对象的方法。 - 访问和修改字段/属性:使用反射读取和修改对象的字段或属性值。
- 获取程序集和模块信息:通过反射获取程序集和模块的详细信息。
尽管反射提供了极大的灵活性,但由于其性能开销较大,因此应谨慎使用,尤其是在性能要求较高的场景中。反射通常用于需要高度灵活性和可扩展性的场景,例如插件框架、动态代理和序列化等。