C#的反射机制是.NET框架提供的重要功能,位于System.Reflection命名空间下,它让程序可以在运行时检查、操作和修改自身的元数据和行为,不需要在编译时就确定要操作的具体类型。反射在很多场景都能发挥作用,比如插件化开发、序列化反序列化、依赖注入框架的实现等。

反射的基础:获取Type对象
要使用反射操作类型,首先需要获取对应类型的Type对象,这是反射的入口。Type类包含了类型的所有元数据信息,比如类名、基类、实现的接口、成员列表等。获取Type对象有三种常用方式:
- 通过对象的
GetType()方法获取,适用于已经有类型实例的场景 - 通过
typeof()运算符获取,适用于编译时已知类型的场景 - 通过
Type.GetType()静态方法获取,传入类型的完全限定名,适用于运行时才知道类型名称的场景
下面是对应的代码示例:
using System;
namespace ReflectionDemo
{
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public void SayHello()
{
Console.WriteLine($"Hello, I am {Name}, {Age} years old");
}
}
class Program
{
static void Main(string[] args)
{
User user = new User();
// 方式1:通过实例的GetType方法获取Type
Type type1 = user.GetType();
Console.WriteLine($"方式1获取的类型名:{type1.Name}");
// 方式2:通过typeof运算符获取Type
Type type2 = typeof(User);
Console.WriteLine($"方式2获取的类型名:{type2.Name}");
// 方式3:通过Type.GetType方法获取Type,需要传入完全限定名
Type type3 = Type.GetType("ReflectionDemo.User");
Console.WriteLine($"方式3获取的类型名:{type3.Name}");
}
}
}
动态获取类型的成员信息
获取到Type对象之后,就可以通过它的属性和方法获取类型的各种成员信息,包括构造函数、属性、方法、字段、事件等。常用的获取成员的方法如下:
GetConstructors():获取类型的所有公共构造函数GetProperties():获取类型的所有公共属性GetMethods():获取类型的所有公共方法GetFields():获取类型的所有公共字段
下面的代码演示了如何获取User类的所有属性和方法信息:
using System;
using System.Reflection;
namespace ReflectionDemo
{
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public void SayHello()
{
Console.WriteLine($"Hello, I am {Name}, {Age} years old");
}
private void PrivateMethod()
{
Console.WriteLine("This is a private method");
}
}
class Program
{
static void Main(string[] args)
{
Type userType = typeof(User);
// 获取所有公共属性
PropertyInfo[] properties = userType.GetProperties();
Console.WriteLine("公共属性列表:");
foreach (PropertyInfo prop in properties)
{
Console.WriteLine($"属性名:{prop.Name},类型:{prop.PropertyType.Name}");
}
// 获取所有公共方法
MethodInfo[] methods = userType.GetMethods();
Console.WriteLine("n公共方法列表:");
foreach (MethodInfo method in methods)
{
Console.WriteLine($"方法名:{method.Name},返回类型:{method.ReturnType.Name}");
}
}
}
}
动态创建类型实例
反射支持在运行时动态创建类型的实例,不需要使用new关键字。常用的方法有两种:
- 调用无参构造函数:使用
Activator.CreateInstance()方法,直接传入Type对象即可 - 调用有参构造函数:使用
Type.GetConstructor()获取对应参数的构造函数,再调用Invoke()方法传入参数创建实例
代码示例:
using System;
using System.Reflection;
namespace ReflectionDemo
{
public class User
{
public string Name { get; set; }
public int Age { get; set; }
// 无参构造函数
public User() { }
// 有参构造函数
public User(string name, int age)
{
Name = name;
Age = age;
}
public void SayHello()
{
Console.WriteLine($"Hello, I am {Name}, {Age} years old");
}
}
class Program
{
static void Main(string[] args)
{
Type userType = typeof(User);
// 调用无参构造函数创建实例
User user1 = (User)Activator.CreateInstance(userType);
user1.Name = "张三";
user1.Age = 20;
user1.SayHello();
// 调用有参构造函数创建实例
// 获取参数为string和int的构造函数
ConstructorInfo constructor = userType.GetConstructor(new Type[] { typeof(string), typeof(int) });
// 传入构造参数创建实例
User user2 = (User)constructor.Invoke(new object[] { "李四", 25 });
user2.SayHello();
}
}
}
动态调用方法和操作属性
反射不仅可以获取成员信息,还可以在运行时动态调用方法、读写属性值,即使这些成员是私有的,也可以通过设置绑定标志来获取和操作。
动态调用方法
调用方法需要先获取MethodInfo对象,然后调用它的Invoke()方法,第一个参数是方法所属的对象实例(静态方法传入null),第二个参数是方法的参数数组。
代码示例:
using System;
using System.Reflection;
namespace ReflectionDemo
{
public class User
{
public string Name { get; set; }
public int Age { get; set; }
public void SayHello()
{
Console.WriteLine($"Hello, I am {Name}, {Age} years old");
}
public int AddAge(int increment)
{
Age += increment;
return Age;
}
private void PrivateMethod()
{
Console.WriteLine("调用了私有方法");
}
}
class Program
{
static void Main(string[] args)
{
Type userType = typeof(User);
User user = new User { Name = "王五", Age = 30 };
// 调用公共无参方法
MethodInfo sayHelloMethod = userType.GetMethod("SayHello");
sayHelloMethod.Invoke(user, null);
// 调用公共有参方法
MethodInfo addAgeMethod = userType.GetMethod("AddAge");
object result = addAgeMethod.Invoke(user, new object[] { 5 });
Console.WriteLine($"调用AddAge后的年龄:{result}");
// 调用私有方法,需要指定BindingFlags
MethodInfo privateMethod = userType.GetMethod("PrivateMethod", BindingFlags.NonPublic | BindingFlags.Instance);
privateMethod.Invoke(user, null);
}
}
}
动态读写属性
操作属性需要先获取PropertyInfo对象,然后通过GetValue()方法读取属性值,通过SetValue()方法设置属性值。
代码示例:
using System;
using System.Reflection;
namespace ReflectionDemo
{
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
class Program
{
static void Main(string[] args)
{
Type userType = typeof(User);
User user = new User();
// 获取Name属性
PropertyInfo nameProp = userType.GetProperty("Name");
// 设置Name属性值
nameProp.SetValue(user, "赵六");
// 读取Name属性值
object nameValue = nameProp.GetValue(user);
Console.WriteLine($"Name属性值:{nameValue}");
// 获取Age属性
PropertyInfo ageProp = userType.GetProperty("Age");
ageProp.SetValue(user, 28);
object ageValue = ageProp.GetValue(user);
Console.WriteLine($"Age属性值:{ageValue}");
}
}
}
反射的性能影响和优化
反射虽然灵活,但是性能比直接编译调用的代码要低,因为反射需要遍历元数据、进行类型检查等操作。如果需要在高频场景使用反射,可以采取以下优化方式:
- 缓存
Type、MethodInfo、PropertyInfo等对象,避免重复获取 - 对于频繁调用的方法,可以使用表达式树或者Emit生成委托,减少反射调用的开销
- 非必要场景不要使用反射,优先考虑泛型、接口等编译时多态方案
下面是一个简单的缓存反射对象的示例:
using System;
using System.Collections.Generic;
using System.Reflection;
namespace ReflectionDemo
{
public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
class ReflectionCache
{
// 缓存Type对象
private static readonly Dictionary<string, Type> TypeCache = new Dictionary<string, Type>();
// 缓存属性信息
private static readonly Dictionary<Type, PropertyInfo[]> PropertyCache = new Dictionary<Type, PropertyInfo[]>();
public static Type GetTypeFromCache(string typeName)
{
if (!TypeCache.ContainsKey(typeName))
{
TypeCache[typeName] = Type.GetType(typeName);
}
return TypeCache[typeName];
}
public static PropertyInfo[] GetPropertiesFromCache(Type type)
{
if (!PropertyCache.ContainsKey(type))
{
PropertyCache[type] = type.GetProperties();
}
return PropertyCache[type];
}
}
class Program
{
static void Main(string[] args)
{
Type userType = ReflectionCache.GetTypeFromCache("ReflectionDemo.User");
PropertyInfo[] properties = ReflectionCache.GetPropertiesFromCache(userType);
Console.WriteLine($"从缓存获取的属性数量:{properties.Length}");
}
}
}
反射的实际应用场景
反射在实际开发中有很多典型的应用场景:
- 插件化开发:主程序通过反射加载外部DLL,动态识别并加载插件类型,不需要主程序依赖插件的具体实现
- 序列化框架:比如JSON序列化工具,通过反射获取对象的属性信息,将对象转换为JSON字符串,或者将JSON字符串转换为对象
- 依赖注入容器:容器通过反射解析类型的构造函数和依赖关系,动态创建对象并注入依赖
- 单元测试框架:测试框架通过反射发现标记了测试特性的方法,动态执行测试用例
合理使用反射可以让代码更加灵活、扩展性更强,但是也要注意不要滥用,避免带来不必要的性能开销和维护难度。
C#Reflection动态获取类型信息调用方法属性修改时间:2026-06-20 17:45:38